diff --git a/src/sql/base/common/codicons.ts b/src/sql/base/common/codicons.ts new file mode 100644 index 0000000000..767dd1c364 --- /dev/null +++ b/src/sql/base/common/codicons.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Set of IDs used to identify SQL contributed icons, which override the default + * codicon behavior vs/base/common/codicons.ts + */ +export const enum SqlIconId { + book = 'book', + dataExplorer = 'dataExplorer', + addServerGroupAction = 'add-server-group-action', + addServerAction = 'add-server-action', + activeConnectionsAction = 'active-connections-action', + serverPage = 'server-page' +} diff --git a/src/sql/workbench/contrib/connection/browser/connection.contribution.ts b/src/sql/workbench/contrib/connection/browser/connection.contribution.ts index 4e086e886c..83dbff1df5 100644 --- a/src/sql/workbench/contrib/connection/browser/connection.contribution.ts +++ b/src/sql/workbench/contrib/connection/browser/connection.contribution.ts @@ -5,11 +5,10 @@ import { IConfigurationRegistry, Extensions as ConfigExtensions } from 'vs/platform/configuration/common/configurationRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; -import { AddServerGroupAction, AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { ClearRecentConnectionsAction, GetCurrentConnectionStringAction } from 'sql/workbench/services/connection/browser/connectionActions'; import * as azdata from 'azdata'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; -import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { localize } from 'vs/nls'; import { ConnectionStatusbarItem } from 'sql/workbench/contrib/connection/browser/connectionStatus'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; @@ -20,12 +19,18 @@ import { integrated, azureMFA } from 'sql/platform/connection/common/constants'; import { AuthenticationType } from 'sql/workbench/services/connection/browser/connectionWidget'; import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { ConnectionViewletPanel } from 'sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel'; +import { ContextKeyEqualsExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ActiveConnectionsFilterAction, AddServerAction, AddServerGroupAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; +import { CONTEXT_SERVER_TREE_VIEW, CONTEXT_SERVER_TREE_HAS_CONNECTIONS } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeView'; +import { SqlIconId } from 'sql/base/common/codicons'; const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(ConnectionStatusbarItem, LifecyclePhase.Restored); import 'sql/workbench/contrib/connection/common/connectionTreeProviderExentionPoint'; +import { ServerTreeViewView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; // Connection Dashboard registration @@ -50,6 +55,15 @@ actionRegistry.registerWorkbenchAction( AddServerGroupAction.LABEL ); +actionRegistry.registerWorkbenchAction( + SyncActionDescriptor.create( + ActiveConnectionsFilterAction, + ActiveConnectionsFilterAction.ID, + ActiveConnectionsFilterAction.SHOW_ACTIVE_CONNECTIONS_LABEL + ), + ActiveConnectionsFilterAction.SHOW_ACTIVE_CONNECTIONS_LABEL +); + actionRegistry.registerWorkbenchAction( SyncActionDescriptor.create( AddServerAction, @@ -59,6 +73,45 @@ actionRegistry.registerWorkbenchAction( AddServerAction.LABEL ); +MenuRegistry.appendMenuItem(MenuId.ViewTitle, { + group: 'navigation', + order: 10, + command: { + id: AddServerAction.ID, + title: AddServerAction.LABEL, + icon: { id: SqlIconId.addServerAction } + }, + when: ContextKeyEqualsExpr.create('view', ConnectionViewletPanel.ID), +}); + +MenuRegistry.appendMenuItem(MenuId.ViewTitle, { + group: 'navigation', + order: 20, + command: { + id: AddServerGroupAction.ID, + title: AddServerGroupAction.LABEL, + icon: { id: SqlIconId.addServerGroupAction } + }, + when: ContextKeyEqualsExpr.create('view', ConnectionViewletPanel.ID), +}); + +MenuRegistry.appendMenuItem(MenuId.ViewTitle, { + group: 'navigation', + order: 30, + command: { + id: ActiveConnectionsFilterAction.ID, + title: ActiveConnectionsFilterAction.SHOW_ACTIVE_CONNECTIONS_LABEL, + icon: { id: SqlIconId.activeConnectionsAction }, + precondition: CONTEXT_SERVER_TREE_HAS_CONNECTIONS, + toggled: { + condition: CONTEXT_SERVER_TREE_VIEW.isEqualTo(ServerTreeViewView.active), + icon: { id: SqlIconId.serverPage }, + tooltip: ActiveConnectionsFilterAction.SHOW_ALL_CONNECTIONS_LABEL + } + }, + when: ContextKeyEqualsExpr.create('view', ConnectionViewletPanel.ID), +}); + CommandsRegistry.registerCommand('azdata.connect', function (accessor, args: { serverName: string, diff --git a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts index b1e6c5445e..bd20daa188 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/connectionViewletPanel.ts @@ -10,12 +10,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IAction } from 'vs/base/common/actions'; import { ServerTreeView } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeView'; -import { - ActiveConnectionsFilterAction, - AddServerAction, AddServerGroupAction -} from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/viewPane'; @@ -33,9 +28,6 @@ export class ConnectionViewletPanel extends ViewPane { private _root?: HTMLElement; private _serverTreeView: ServerTreeView; - private _addServerAction: IAction; - private _addServerGroupAction: IAction; - private _activeConnectionsFilterAction: ActiveConnectionsFilterAction; constructor( private options: IViewletViewOptions, @@ -52,18 +44,11 @@ export class ConnectionViewletPanel extends ViewPane { @ITelemetryService telemetryService: ITelemetryService, ) { super({ ...(options as IViewPaneOptions) }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, opener, themeService, telemetryService); - this._addServerAction = this.instantiationService.createInstance(AddServerAction, - AddServerAction.ID, - AddServerAction.LABEL); - this._addServerGroupAction = this.instantiationService.createInstance(AddServerGroupAction, - AddServerGroupAction.ID, - AddServerGroupAction.LABEL); - this._serverTreeView = this.objectExplorerService.getServerTreeView() as ServerTreeView; + this._serverTreeView = this.objectExplorerService.getServerTreeView() as ServerTreeView; if (!this._serverTreeView) { this._serverTreeView = this.instantiationService.createInstance(ServerTreeView); this.objectExplorerService.registerServerTreeView(this._serverTreeView); } - this._activeConnectionsFilterAction = this._serverTreeView.activeConnectionsFilterAction; } protected renderHeader(container: HTMLElement): void { @@ -114,14 +99,6 @@ export class ConnectionViewletPanel extends ViewPane { return 0; } - public getActions(): IAction[] { - return [ - this._addServerAction, - this._addServerGroupAction, - this._activeConnectionsFilterAction - ]; - } - public clearSearch() { this._serverTreeView.refreshTree(); } diff --git a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts index e999620a60..df469ee37c 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet.ts @@ -27,6 +27,7 @@ import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneCont import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { Viewlet } from 'vs/workbench/browser/viewlet'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; +import { SqlIconId } from 'sql/base/common/codicons'; export const VIEWLET_ID = 'workbench.view.connections'; @@ -132,8 +133,6 @@ export class DataExplorerViewPaneContainer extends ViewPaneContainer { } } -export const dataExplorerIconId = 'dataExplorer'; - export const VIEW_CONTAINER = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer({ id: VIEWLET_ID, title: localize('dataexplorer.name', "Connections"), @@ -144,7 +143,7 @@ export const VIEW_CONTAINER = Registry.as(ViewContainer keybindings: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_D }, order: 0 }, - icon: { id: 'dataExplorer' }, + icon: { id: SqlIconId.dataExplorer }, order: 0, storageId: `${VIEWLET_ID}.state` }, ViewContainerLocation.Sidebar, { isDefault: true }); diff --git a/src/sql/workbench/contrib/objectExplorer/browser/media/serverTreeActions.css b/src/sql/workbench/contrib/objectExplorer/browser/media/serverTreeActions.css index 6967b2bd11..f39c411127 100644 --- a/src/sql/workbench/contrib/objectExplorer/browser/media/serverTreeActions.css +++ b/src/sql/workbench/contrib/objectExplorer/browser/media/serverTreeActions.css @@ -4,29 +4,38 @@ *--------------------------------------------------------------------------------------------*/ /* Icons for various registered servers actions */ -.monaco-workbench .add-server-action { - background-image: url('add_server.svg'); +.monaco-workbench .dataExplorer-viewlet .actions-container .action-label.add-server-action { + background-image: url('add_server.svg') !important; } -.monaco-workbench.vs-dark .add-server-action, -.monaco-workbench.hc-black .add-server-action { - background-image: url('add_server_inverse.svg'); +.monaco-workbench.vs-dark .dataExplorer-viewlet .actions-container .action-label.add-server-action, +.monaco-workbench.hc-black .dataExplorer-viewlet .actions-container .action-label.add-server-action { + background-image: url('add_server_inverse.svg') !important; } -.monaco-workbench .add-server-group-action { - background-image: url('new_servergroup.svg'); +.monaco-workbench .dataExplorer-viewlet .actions-container .action-label.add-server-group-action { + background-image: url('new_servergroup.svg') !important; } -.monaco-workbench.vs-dark .add-server-group-action, -.monaco-workbench.hc-black .add-server-group-action { - background-image: url('new_servergroup_inverse.svg'); +.monaco-workbench.vs-dark .dataExplorer-viewlet .actions-container .action-label.add-server-group-action, +.monaco-workbench.hc-black .dataExplorer-viewlet .actions-container .action-label.add-server-group-action { + background-image: url('new_servergroup_inverse.svg') !important; } -.monaco-workbench .active-connections-action { - background-image: url('connected_active_server.svg'); +.monaco-workbench .dataExplorer-viewlet .actions-container .action-label.active-connections-action { + background-image: url('connected_active_server.svg') !important; } -.monaco-workbench.vs-dark .active-connections-action, -.monaco-workbench.hc-black .active-connections-action{ - background-image: url('connected_active_server_inverse.svg'); +.monaco-workbench.vs-dark .dataExplorer-viewlet .actions-container .action-label.active-connections-action, +.monaco-workbench.hc-black .dataExplorer-viewlet .actions-container .action-label.active-connections-action { + background-image: url('connected_active_server_inverse.svg') !important; +} + +.monaco-workbench .dataExplorer-viewlet .actions-container .action-label.server-page { + background-image: url('server_page.svg') !important; +} + +.monaco-workbench.vs-dark .dataExplorer-viewlet .actions-container .action-label.server-page, +.monaco-workbench.hc-black .dataExplorer-viewlet .actions-container .action-label.server-page { + background-image: url('server_page_inverse.svg') !important; } diff --git a/src/sql/workbench/contrib/objectExplorer/browser/media/server_page.svg b/src/sql/workbench/contrib/objectExplorer/browser/media/server_page.svg new file mode 100644 index 0000000000..452693ba2b --- /dev/null +++ b/src/sql/workbench/contrib/objectExplorer/browser/media/server_page.svg @@ -0,0 +1 @@ +server_16x16 \ No newline at end of file diff --git a/src/sql/workbench/contrib/objectExplorer/browser/media/server_page_inverse.svg b/src/sql/workbench/contrib/objectExplorer/browser/media/server_page_inverse.svg new file mode 100644 index 0000000000..c11b61e1cd --- /dev/null +++ b/src/sql/workbench/contrib/objectExplorer/browser/media/server_page_inverse.svg @@ -0,0 +1 @@ +server_inverse_16x16 \ No newline at end of file diff --git a/src/sql/workbench/contrib/objectExplorer/browser/serverTreeView.ts b/src/sql/workbench/contrib/objectExplorer/browser/serverTreeView.ts index ae26232d9e..662ffc2564 100644 --- a/src/sql/workbench/contrib/objectExplorer/browser/serverTreeView.ts +++ b/src/sql/workbench/contrib/objectExplorer/browser/serverTreeView.ts @@ -19,12 +19,11 @@ import { append, $, hide, show } from 'vs/base/browser/dom'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import * as ConnectionUtils from 'sql/platform/connection/common/utils'; -import { ActiveConnectionsFilterAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement'; import { TreeCreationUtils } from 'sql/workbench/services/objectExplorer/browser/treeCreationUtils'; import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils'; import { TreeSelectionHandler } from 'sql/workbench/services/objectExplorer/browser/treeSelectionHandler'; -import { IObjectExplorerService, IServerTreeView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; +import { IObjectExplorerService, IServerTreeView, ServerTreeViewView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { Button } from 'sql/base/browser/ui/button/button'; import { TreeNode, TreeItemCollapsibleState } from 'sql/workbench/services/objectExplorer/common/treeNode'; @@ -42,6 +41,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; import { coalesce } from 'vs/base/common/arrays'; import { CONNECTIONS_SORT_BY_CONFIG_KEY } from 'sql/platform/connection/common/connectionConfig'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; + +export const CONTEXT_SERVER_TREE_VIEW = new RawContextKey('serverTreeView.view', ServerTreeViewView.all); +export const CONTEXT_SERVER_TREE_HAS_CONNECTIONS = new RawContextKey('serverTreeView.hasConnections', false); /** * ServerTreeview implements the dynamic tree view. @@ -51,10 +54,11 @@ export class ServerTreeView extends Disposable implements IServerTreeView { public messages?: HTMLElement; private _buttonSection?: HTMLElement; private _treeSelectionHandler: TreeSelectionHandler; - private _activeConnectionsFilterAction: ActiveConnectionsFilterAction; private _tree?: ITree | AsyncServerTree; private _onSelectionOrFocusChange: Emitter; private _actionProvider: ServerTreeActionProvider; + private _viewKey: IContextKey; + private _hasConnectionsKey: IContextKey; constructor( @IConnectionManagementService private _connectionManagementService: IConnectionManagementService, @@ -65,14 +69,12 @@ export class ServerTreeView extends Disposable implements IServerTreeView { @IConfigurationService private _configurationService: IConfigurationService, @ICapabilitiesService capabilitiesService: ICapabilitiesService, @IContextMenuService private _contextMenuService: IContextMenuService, - @IKeybindingService private _keybindingService: IKeybindingService + @IKeybindingService private _keybindingService: IKeybindingService, + @IContextKeyService contextKeyService: IContextKeyService ) { super(); - this._activeConnectionsFilterAction = this._instantiationService.createInstance( - ActiveConnectionsFilterAction, - ActiveConnectionsFilterAction.ID, - ActiveConnectionsFilterAction.LABEL, - this); + this._hasConnectionsKey = CONTEXT_SERVER_TREE_HAS_CONNECTIONS.bindTo(contextKeyService); + this._viewKey = CONTEXT_SERVER_TREE_VIEW.bindTo(contextKeyService); this._treeSelectionHandler = this._instantiationService.createInstance(TreeSelectionHandler); this._onSelectionOrFocusChange = new Emitter(); this._actionProvider = this._instantiationService.createInstance(ServerTreeActionProvider); @@ -93,11 +95,8 @@ export class ServerTreeView extends Disposable implements IServerTreeView { this.registerCommands(); } - /** - * Get active connections filter action - */ - public get activeConnectionsFilterAction(): ActiveConnectionsFilterAction { - return this._activeConnectionsFilterAction; + public get view(): ServerTreeViewView { + return this._viewKey.get(); } /** @@ -146,7 +145,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView { hide(this.messages); if (!this._connectionManagementService.hasRegisteredServers()) { - this._activeConnectionsFilterAction.enabled = false; this._buttonSection = append(container, $('.button-section')); const connectButton = new Button(this._buttonSection); connectButton.label = localize('serverTree.addConnection', "Add Connection"); @@ -248,7 +246,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView { private async handleAddConnectionProfile(newProfile?: IConnectionProfile): Promise { if (this._buttonSection) { hide(this._buttonSection); - this._activeConnectionsFilterAction.enabled = true; } if (this._tree instanceof AsyncServerTree) { @@ -369,7 +366,8 @@ export class ServerTreeView extends Disposable implements IServerTreeView { public async refreshTree(): Promise { hide(this.messages!); - this.clearOtherActions(); + this._viewKey.set(ServerTreeViewView.all); + this._hasConnectionsKey.set(this._connectionManagementService.hasRegisteredServers()); return TreeUpdateUtils.registeredServerUpdate(this._tree!, this._connectionManagementService); } @@ -419,10 +417,9 @@ export class ServerTreeView extends Disposable implements IServerTreeView { /** * Set tree elements based on the view (recent/active) */ - public showFilteredTree(view: string): void { + public showFilteredTree(view: ServerTreeViewView): void { hide(this.messages!); - // Clear other action views if user switched between two views - this.clearOtherActions(view); + this._viewKey.set(view); const root = TreeUpdateUtils.getTreeInput(this._connectionManagementService); let treeInput: ConnectionProfileGroup | undefined = undefined; if (root) { @@ -466,7 +463,7 @@ export class ServerTreeView extends Disposable implements IServerTreeView { } hide(this.messages!); // Clear other actions if user searched during other views - this.clearOtherActions(); + this._viewKey.set(ServerTreeViewView.all); // Filter connections based on search const filteredResults = this.searchConnections(searchString); if (!filteredResults || filteredResults.length === 0) { @@ -533,18 +530,6 @@ export class ServerTreeView extends Disposable implements IServerTreeView { return false; } - /** - * Clears the toggle icons for active and recent - */ - private clearOtherActions(view?: string) { - if (!view) { - this._activeConnectionsFilterAction.isSet = false; - } - if (view === 'recent') { - this._activeConnectionsFilterAction.isSet = false; - } - } - private onSelected(event: any): void { this._treeSelectionHandler.onTreeSelect(event, this._tree!, this._connectionManagementService, this._objectExplorerService, () => this._onSelectionOrFocusChange.fire()); this._onSelectionOrFocusChange.fire(); diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts index 28725e9b61..9f97d9058a 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/connectionTreeActions.test.ts @@ -8,8 +8,7 @@ import * as assert from 'assert'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import { - RefreshAction, EditConnectionAction, AddServerAction, DeleteConnectionAction, DisconnectConnectionAction, - ActiveConnectionsFilterAction, RecentConnectionsFilterAction + RefreshAction, EditConnectionAction, DeleteConnectionAction, DisconnectConnectionAction, ActiveConnectionsFilterAction, AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService'; @@ -17,7 +16,7 @@ import { TestErrorMessageService } from 'sql/platform/errorMessage/test/common/t import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServerTreeView } from 'sql/workbench/contrib/objectExplorer/browser/serverTreeView'; import * as LocalizedConstants from 'sql/workbench/services/connection/browser/localizedConstants'; -import { ObjectExplorerService, ObjectExplorerNodeEventArgs } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; +import { ObjectExplorerService, ObjectExplorerNodeEventArgs, ServerTreeViewView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'; import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType'; import { Emitter, Event } from 'vs/base/common/event'; @@ -263,12 +262,15 @@ suite('SQL Connection Tree Action tests', () => { return new Promise((resolve) => resolve({})); }); - let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); - serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); + let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService, undefined, undefined, new MockContextKeyService()); + serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAny())); serverTreeView.setup(x => x.refreshTree()); - let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object); + serverTreeView.setup(x => x.view).returns(() => ServerTreeViewView.all); + const mockObjectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService); + mockObjectExplorerService.setup(x => x.getServerTreeView()).returns(() => serverTreeView.object); + let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.SHOW_ACTIVE_CONNECTIONS_LABEL, mockObjectExplorerService.object); return connectionTreeAction.run().then((value) => { - serverTreeView.verify(x => x.showFilteredTree('active'), TypeMoq.Times.once()); + serverTreeView.verify(x => x.showFilteredTree(ServerTreeViewView.active), TypeMoq.Times.once()); }); }); @@ -278,42 +280,13 @@ suite('SQL Connection Tree Action tests', () => { return new Promise((resolve) => resolve({})); }); - let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); - serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); + let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService, undefined, undefined, new MockContextKeyService()); + serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAny())); serverTreeView.setup(x => x.refreshTree()); - let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.LABEL, serverTreeView.object); - connectionTreeAction.isSet = true; - return connectionTreeAction.run().then((value) => { - serverTreeView.verify(x => x.refreshTree(), TypeMoq.Times.once()); - }); - }); - - test('RecentConnectionsFilterAction - test if view is called to display filtered results', () => { - let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); - instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new Promise((resolve) => resolve({})); - }); - - let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); - serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); - serverTreeView.setup(x => x.refreshTree()); - let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object); - return connectionTreeAction.run().then((value) => { - serverTreeView.verify(x => x.showFilteredTree('recent'), TypeMoq.Times.once()); - }); - }); - - test('RecentConnectionsFilterAction - test if view is called refresh results if action is toggled', () => { - let instantiationService = TypeMoq.Mock.ofType(InstantiationService, TypeMoq.MockBehavior.Loose); - instantiationService.setup(x => x.createInstance(TypeMoq.It.isAny())).returns((input) => { - return new Promise((resolve) => resolve({})); - }); - - let serverTreeView = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Strict, undefined, instantiationService.object, undefined, undefined, undefined, undefined, capabilitiesService); - serverTreeView.setup(x => x.showFilteredTree(TypeMoq.It.isAnyString())); - serverTreeView.setup(x => x.refreshTree()); - let connectionTreeAction: RecentConnectionsFilterAction = new RecentConnectionsFilterAction(RecentConnectionsFilterAction.ID, RecentConnectionsFilterAction.LABEL, serverTreeView.object); - connectionTreeAction.isSet = true; + serverTreeView.setup(x => x.view).returns(() => ServerTreeViewView.active); + const mockObjectExplorerService = TypeMoq.Mock.ofType(ObjectExplorerService); + mockObjectExplorerService.setup(x => x.getServerTreeView()).returns(() => serverTreeView.object); + let connectionTreeAction: ActiveConnectionsFilterAction = new ActiveConnectionsFilterAction(ActiveConnectionsFilterAction.ID, ActiveConnectionsFilterAction.SHOW_ACTIVE_CONNECTIONS_LABEL, mockObjectExplorerService.object); return connectionTreeAction.run().then((value) => { serverTreeView.verify(x => x.refreshTree(), TypeMoq.Times.once()); }); diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/serverTreeView.test.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/serverTreeView.test.ts index 2e5034b0ec..212a83d2b3 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/serverTreeView.test.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/serverTreeView.test.ts @@ -16,6 +16,7 @@ import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServic import { TreeItemCollapsibleState } from 'sql/workbench/services/objectExplorer/common/treeNode'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import * as assert from 'assert'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; suite('ServerTreeView onAddConnectionProfile handler tests', () => { @@ -37,7 +38,7 @@ suite('ServerTreeView onAddConnectionProfile handler tests', () => { ); mockConnectionManagementService.setup(x => x.getConnectionGroups()).returns(x => []); mockConnectionManagementService.setup(x => x.hasRegisteredServers()).returns(() => true); - serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, undefined, capabilitiesService, undefined, undefined); + serverTreeView = new ServerTreeView(mockConnectionManagementService.object, instantiationService, undefined, new TestThemeService(), undefined, undefined, capabilitiesService, undefined, undefined, new MockContextKeyService()); mockTree = TypeMoq.Mock.ofType(TestTree); (serverTreeView as any)._tree = mockTree.object; mockRefreshTreeMethod = TypeMoq.Mock.ofType(Function); diff --git a/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts b/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts index 5533627ee0..1f91d11340 100644 --- a/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts +++ b/src/sql/workbench/contrib/scripting/test/browser/scriptingActions.test.ts @@ -22,6 +22,7 @@ import { createObjectExplorerServiceMock } from 'sql/workbench/services/objectEx import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { TestTree } from 'sql/workbench/test/treeMock'; import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService'; +import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; const connection: azdata.IConnectionProfile = { options: [], @@ -64,7 +65,7 @@ suite('Scripting Actions', () => { instantiationService = new InstantiationService(collection); const capabilitiesService = new TestCapabilitiesService(); const connectionManagementServiceMock = TypeMoq.Mock.ofType(TestConnectionManagementService, TypeMoq.MockBehavior.Loose); - const serverTreeViewMock = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Loose, connectionManagementServiceMock.object, instantiationService, undefined, undefined, undefined, undefined, capabilitiesService); + const serverTreeViewMock = TypeMoq.Mock.ofType(ServerTreeView, TypeMoq.MockBehavior.Loose, connectionManagementServiceMock.object, instantiationService, undefined, undefined, undefined, undefined, capabilitiesService, undefined, undefined, new MockContextKeyService()); treeMock = TypeMoq.Mock.ofType(TestTree); serverTreeViewMock.setup(x => x.tree).returns(() => treeMock.object); collection.set(IObjectExplorerService, createObjectExplorerServiceMock({ serverTreeView: serverTreeViewMock.object, treeNode: treeNode })); diff --git a/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts b/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts index 66ef70f570..4ae17da5a8 100644 --- a/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts +++ b/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { escape } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; @@ -37,7 +38,7 @@ export default () => `
- +

${escape(localize('welcomePage.createConnection', "Create a connection"))}

${escape(localize('welcomePage.createConnectionBody', "Connect to a database instance through the connection dialog."))}

diff --git a/src/sql/workbench/contrib/welcome/page/browser/gettingStartedTour.ts b/src/sql/workbench/contrib/welcome/page/browser/gettingStartedTour.ts index bee9b7349a..4d5bbabe9d 100644 --- a/src/sql/workbench/contrib/welcome/page/browser/gettingStartedTour.ts +++ b/src/sql/workbench/contrib/welcome/page/browser/gettingStartedTour.ts @@ -22,8 +22,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { Button } from 'sql/base/browser/ui/button/button'; import { extensionsViewIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { settingsViewBarIcon } from 'vs/workbench/browser/parts/activitybar/activitybarPart'; -import { dataExplorerIconId } from 'sql/workbench/contrib/dataExplorer/browser/dataExplorerViewlet'; import { notebookIconId } from 'sql/workbench/contrib/notebook/browser/notebookExplorer/notebookExplorerViewlet'; +import { SqlIconId } from 'sql/base/common/codicons'; const $ = dom.$; interface TourData { @@ -44,7 +44,7 @@ interface TourData { popupImage: string; } -const dataExplorerIconCssSelector = `.action-label.${dataExplorerIconId}`; +const dataExplorerIconCssSelector = `.action-label.${SqlIconId.dataExplorer}`; const notebookIconCssSelector = `.action-label.${notebookIconId}`; const extensionsIconCssSelector = ThemeIcon.asCSSSelector(extensionsViewIcon); const settingsGearIconCssSelector = ThemeIcon.asCSSSelector(settingsViewBarIcon); diff --git a/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts index b0eb746193..d843e6908e 100644 --- a/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -54,6 +54,7 @@ import { ICommandAction, MenuItemAction } from 'vs/platform/actions/common/actio import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionRecommendations/common/extensionRecommendations'; import { attachButtonStyler } from 'vs/platform/theme/common/styler'; +import { AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; const telemetryFrom = 'welcomePage'; @@ -211,7 +212,7 @@ const extensionPackStrings = { const NewActionItems: ICommandAction[] = [ { title: localize('welcomePage.newConnection', "New connection"), - id: 'registeredServers.addConnection' + id: AddServerAction.ID }, { title: localize('welcomePage.newQuery', "New query"), id: 'workbench.action.files.newUntitledFile' diff --git a/src/sql/workbench/contrib/welcome2/page/browser/az_data_welcome_page.ts b/src/sql/workbench/contrib/welcome2/page/browser/az_data_welcome_page.ts index 537e3be962..069dbc5250 100644 --- a/src/sql/workbench/contrib/welcome2/page/browser/az_data_welcome_page.ts +++ b/src/sql/workbench/contrib/welcome2/page/browser/az_data_welcome_page.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { escape } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; @@ -18,7 +19,7 @@ export default () => `

${escape(localize('welcomePage.start', "Start"))}

    -
  • ${escape(localize('welcomePage.newConnection', "New connection"))}
  • +
  • ${escape(localize('welcomePage.newConnection', "New connection"))}
  • ${escape(localize('welcomePage.newQuery', "New query"))}
  • ${escape(localize('welcomePage.newNotebook', "New notebook"))}
  • ${escape(localize('welcomePage.openFileMac', "Open file"))}
  • diff --git a/src/sql/workbench/services/objectExplorer/browser/connectionTreeAction.ts b/src/sql/workbench/services/objectExplorer/browser/connectionTreeAction.ts index 2571ec4a46..bf77e29c9c 100644 --- a/src/sql/workbench/services/objectExplorer/browser/connectionTreeAction.ts +++ b/src/sql/workbench/services/objectExplorer/browser/connectionTreeAction.ts @@ -10,7 +10,7 @@ import { IConnectionManagementService } from 'sql/platform/connection/common/con import { IConnectionProfile } from 'sql/platform/connection/common/interfaces'; import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { ITree } from 'vs/base/parts/tree/browser/tree'; -import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; +import { IObjectExplorerService, ServerTreeViewView } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'; import Severity from 'vs/base/common/severity'; import { ObjectExplorerActionsContext } from 'sql/workbench/services/objectExplorer/browser/objectExplorerActions'; @@ -19,6 +19,7 @@ import { UNSAVED_GROUP_ID } from 'sql/platform/connection/common/constants'; import { IServerGroupController } from 'sql/platform/serverGroup/common/serverGroupController'; import { ILogService } from 'vs/platform/log/common/log'; import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; +import { SqlIconId } from 'sql/base/common/codicons'; export interface IServerView { showFilteredTree(filter: string): void; @@ -158,8 +159,7 @@ export class AddServerAction extends Action { label: string, @IConnectionManagementService private _connectionManagementService: IConnectionManagementService ) { - super(id, label); - this.class = 'add-server-action'; + super(id, label, SqlIconId.addServerAction); } public async run(element: ConnectionProfileGroup): Promise { @@ -187,7 +187,7 @@ export class AddServerAction extends Action { } /** - * Actions to add a server to the group + * Action to open up the dialog to create a new server group */ export class AddServerGroupAction extends Action { public static ID = 'registeredServers.addServerGroup'; @@ -198,8 +198,7 @@ export class AddServerGroupAction extends Action { label: string, @IServerGroupController private readonly serverGroupController: IServerGroupController ) { - super(id, label); - this.class = 'add-server-group-action'; + super(id, label, SqlIconId.addServerGroupAction); } public async run(): Promise { @@ -232,96 +231,33 @@ export class EditServerGroupAction extends Action { } /** - * Display active connections in the tree + * Action to toggle filtering the server connections tree to only show + * active connections or not. */ export class ActiveConnectionsFilterAction extends Action { public static ID = 'registeredServers.recentConnections'; - public static LABEL = localize('activeConnections', "Show Active Connections"); - private static enabledClass = 'active-connections-action'; - private static disabledClass = 'icon server-page'; - private static showAllConnectionsLabel = localize('showAllConnections', "Show All Connections"); - private _isSet: boolean = false; + public static SHOW_ACTIVE_CONNECTIONS_LABEL = localize('activeConnections', "Show Active Connections"); + public static SHOW_ALL_CONNECTIONS_LABEL = localize('showAllConnections', "Show All Connections"); public static readonly ACTIVE = 'active'; - public get isSet(): boolean { - return this._isSet; - } - public set isSet(value: boolean) { - this._isSet = value; - this.class = (!this._isSet) ? - ActiveConnectionsFilterAction.enabledClass : ActiveConnectionsFilterAction.disabledClass; - } constructor( id: string, label: string, - private view: IServerView + @IObjectExplorerService private _objectExplorerService: IObjectExplorerService ) { - super(id, label); - this.class = ActiveConnectionsFilterAction.enabledClass; + super(id, label, SqlIconId.activeConnectionsAction); } - public run(): Promise { - if (!this.view) { - // return without doing anything - return Promise.resolve(true); - } - if (this.class === ActiveConnectionsFilterAction.enabledClass) { + public async run(): Promise { + const serverTreeView = this._objectExplorerService.getServerTreeView(); + if (serverTreeView.view !== ServerTreeViewView.active) { // show active connections in the tree - this.view.showFilteredTree(ActiveConnectionsFilterAction.ACTIVE); - this.isSet = true; - this.label = ActiveConnectionsFilterAction.showAllConnectionsLabel; + serverTreeView.showFilteredTree(ServerTreeViewView.active); } else { // show full tree - this.view.refreshTree(); - this.isSet = false; - this.label = ActiveConnectionsFilterAction.LABEL; + await serverTreeView.refreshTree(); } - return Promise.resolve(true); - } -} - -/** - * Display recent connections in the tree - */ -export class RecentConnectionsFilterAction extends Action { - public static ID = 'registeredServers.recentConnections'; - public static LABEL = localize('recentConnections', "Recent Connections"); - private static enabledClass = 'recent-connections-action'; - private static disabledClass = 'recent-connections-action-set'; - private _isSet: boolean; - public get isSet(): boolean { - return this._isSet; - } - public set isSet(value: boolean) { - this._isSet = value; - this.class = (!this._isSet) ? - RecentConnectionsFilterAction.enabledClass : RecentConnectionsFilterAction.disabledClass; - } - constructor( - id: string, - label: string, - private view: IServerView - ) { - super(id, label); - this.class = RecentConnectionsFilterAction.enabledClass; - this._isSet = false; - } - - public run(): Promise { - if (!this.view) { - // return without doing anything - return Promise.resolve(true); - } - if (this.class === RecentConnectionsFilterAction.enabledClass) { - // show recent connections in the tree - this.view.showFilteredTree('recent'); - this.isSet = true; - } else { - // show full tree - this.view.refreshTree(); - this.isSet = false; - } - return Promise.resolve(true); + return true; } } diff --git a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts index 176d2a463e..a225845590 100644 --- a/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts +++ b/src/sql/workbench/services/objectExplorer/browser/objectExplorerService.ts @@ -20,7 +20,6 @@ import { entries } from 'sql/base/common/collections'; import { values } from 'vs/base/common/collections'; import { startsWith } from 'vs/base/common/strings'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; -import { IAction } from 'vs/base/common/actions'; import { ServerTreeActionProvider } from 'sql/workbench/services/objectExplorer/browser/serverTreeActionProvider'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { AsyncServerTree, ServerTreeElement } from 'sql/workbench/services/objectExplorer/browser/asyncServerTree'; @@ -33,6 +32,11 @@ export interface NodeExpandInfoWithProviderId extends azdata.ObjectExplorerExpan providerId: string; } +export const enum ServerTreeViewView { + all = 'all', + active = 'active' +} + export interface IServerTreeView { readonly tree: ITree | AsyncServerTree; readonly onSelectionOrFocusChange: Event; @@ -47,9 +51,10 @@ export interface IServerTreeView { setExpandedState(node: ServerTreeElement, state?: TreeItemCollapsibleState): Promise; setSelected(node: ServerTreeElement, selected?: boolean, clearOtherSelections?: boolean): Promise; refreshTree(): Promise; - readonly activeConnectionsFilterAction: IAction; renderBody(container: HTMLElement): Promise; layout(size: number): void; + showFilteredTree(view: ServerTreeViewView): void; + view: ServerTreeViewView; } export interface IObjectExplorerService { diff --git a/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts b/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts index d009bd189f..4c6606b014 100644 --- a/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts +++ b/src/sql/workbench/services/objectExplorer/browser/serverTreeActionProvider.ts @@ -9,8 +9,8 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { - DisconnectConnectionAction, AddServerAction, EditConnectionAction, - DeleteConnectionAction, RefreshAction, EditServerGroupAction + DisconnectConnectionAction, EditConnectionAction, + DeleteConnectionAction, RefreshAction, EditServerGroupAction, AddServerAction } from 'sql/workbench/services/objectExplorer/browser/connectionTreeAction'; import { TreeNode } from 'sql/workbench/services/objectExplorer/common/treeNode'; import { NodeType } from 'sql/workbench/services/objectExplorer/common/nodeType'; @@ -157,6 +157,7 @@ export class ServerTreeActionProvider { * Return actions for connection group elements */ private getConnectionProfileGroupActions(element: ConnectionProfileGroup): IAction[] { + // TODO: Should look into using the MenuRegistry for this return [ this._instantiationService.createInstance(AddServerAction, AddServerAction.ID, AddServerAction.LABEL), this._instantiationService.createInstance(EditServerGroupAction, EditServerGroupAction.ID, EditServerGroupAction.LABEL, element), diff --git a/src/vs/base/common/codicons.ts b/src/vs/base/common/codicons.ts index 2a835cc81d..1142c7c628 100644 --- a/src/vs/base/common/codicons.ts +++ b/src/vs/base/common/codicons.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { SqlIconId } from 'sql/base/common/codicons'; import { codiconStartMarker } from 'vs/base/common/codicon'; import { Emitter, Event } from 'vs/base/common/event'; import { localize } from 'vs/nls'; @@ -91,9 +92,9 @@ export namespace CSSIcon { let [, id, modifier] = match; // {{SQL CARBON EDIT}} Modifying method to not add 'codicon' in front of sql carbon icons. - let sqlCarbonIcons = ['book', 'dataExplorer']; + let sqlCarbonIcons: string[] = [SqlIconId.book, SqlIconId.dataExplorer, SqlIconId.activeConnectionsAction, SqlIconId.addServerAction, SqlIconId.addServerGroupAction, SqlIconId.serverPage]; if (sqlCarbonIcons.includes(id)) { - return [id]; + return ['codicon', id]; // {{SQL CARBON EDIT}} End of edit } else { const classNames = ['codicon', 'codicon-' + id];