Hide results tab when there are none (#5763)

* wip

* add behavior around hiding results when there are none

* fix strict null access
This commit is contained in:
Anthony Dresser
2019-05-31 11:18:33 -07:00
committed by GitHub
parent 1773dede25
commit 559c675164
2 changed files with 64 additions and 17 deletions

View File

@@ -13,6 +13,8 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeyCode } from 'vs/base/common/keyCodes'; import { KeyCode } from 'vs/base/common/keyCodes';
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Color } from 'vs/base/common/color'; import { Color } from 'vs/base/common/color';
import { isUndefinedOrNull } from 'vs/base/common/types';
import * as map from 'vs/base/common/map';
export interface ITabbedPanelStyles { export interface ITabbedPanelStyles {
titleActiveForeground?: Color; titleActiveForeground?: Color;
@@ -39,7 +41,8 @@ export interface IPanelTab {
view: IPanelView; view: IPanelView;
} }
interface IInternalPanelTab extends IPanelTab { interface IInternalPanelTab {
tab: IPanelTab;
header: HTMLElement; header: HTMLElement;
disposables: IDisposable[]; disposables: IDisposable[];
label: HTMLElement; label: HTMLElement;
@@ -108,11 +111,11 @@ export class TabbedPanel extends Disposable {
return this._tabMap.has(tab.identifier); return this._tabMap.has(tab.identifier);
} }
public pushTab(tab: IPanelTab): PanelTabIdentifier { public pushTab(tab: IPanelTab, index?: number): PanelTabIdentifier {
let internalTab = tab as IInternalPanelTab; let internalTab = { tab } as IInternalPanelTab;
internalTab.disposables = []; internalTab.disposables = [];
this._tabMap.set(tab.identifier, internalTab); this._tabMap.set(tab.identifier, internalTab);
this._createTab(internalTab); this._createTab(internalTab, index);
if (!this._shownTabId) { if (!this._shownTabId) {
this.showTab(tab.identifier); this.showTab(tab.identifier);
} }
@@ -132,26 +135,31 @@ export class TabbedPanel extends Disposable {
this._actionbar.context = context; this._actionbar.context = context;
} }
private _createTab(tab: IInternalPanelTab): void { private _createTab(tab: IInternalPanelTab, index?: number): void {
let tabHeaderElement = DOM.$('.tab-header'); let tabHeaderElement = DOM.$('.tab-header');
tabHeaderElement.setAttribute('tabindex', '0'); tabHeaderElement.setAttribute('tabindex', '0');
tabHeaderElement.setAttribute('role', 'tab'); tabHeaderElement.setAttribute('role', 'tab');
tabHeaderElement.setAttribute('aria-selected', 'false'); tabHeaderElement.setAttribute('aria-selected', 'false');
tabHeaderElement.setAttribute('aria-controls', tab.identifier); tabHeaderElement.setAttribute('aria-controls', tab.tab.identifier);
let tabElement = DOM.$('.tab'); let tabElement = DOM.$('.tab');
tabHeaderElement.appendChild(tabElement); tabHeaderElement.appendChild(tabElement);
let tabLabel = DOM.$('a.tabLabel'); let tabLabel = DOM.$('a.tabLabel');
tabLabel.innerText = tab.title; tabLabel.innerText = tab.tab.title;
tabElement.appendChild(tabLabel); tabElement.appendChild(tabLabel);
tab.disposables.push(DOM.addDisposableListener(tabHeaderElement, DOM.EventType.CLICK, e => this.showTab(tab.identifier))); tab.disposables.push(DOM.addDisposableListener(tabHeaderElement, DOM.EventType.CLICK, e => this.showTab(tab.tab.identifier)));
tab.disposables.push(DOM.addDisposableListener(tabHeaderElement, DOM.EventType.KEY_UP, (e: KeyboardEvent) => { tab.disposables.push(DOM.addDisposableListener(tabHeaderElement, DOM.EventType.KEY_UP, (e: KeyboardEvent) => {
let event = new StandardKeyboardEvent(e); let event = new StandardKeyboardEvent(e);
if (event.equals(KeyCode.Enter)) { if (event.equals(KeyCode.Enter)) {
this.showTab(tab.identifier); this.showTab(tab.tab.identifier);
e.stopImmediatePropagation(); e.stopImmediatePropagation();
} }
})); }));
this.tabList.appendChild(tabHeaderElement); const insertBefore = !isUndefinedOrNull(index) ? this.tabList.children.item(index) : undefined;
if (insertBefore) {
this.tabList.insertBefore(tabHeaderElement, insertBefore);
} else {
this.tabList.append(tabHeaderElement);
}
tab.header = tabHeaderElement; tab.header = tabHeaderElement;
tab.label = tabLabel; tab.label = tabLabel;
} }
@@ -178,10 +186,10 @@ export class TabbedPanel extends Disposable {
tab.body = DOM.$('.tab-container'); tab.body = DOM.$('.tab-container');
tab.body.style.width = '100%'; tab.body.style.width = '100%';
tab.body.style.height = '100%'; tab.body.style.height = '100%';
tab.view.render(tab.body); tab.tab.view.render(tab.body);
} }
this.body.appendChild(tab.body); this.body.appendChild(tab.body);
this.body.setAttribute('aria-labelledby', tab.identifier); this.body.setAttribute('aria-labelledby', tab.tab.identifier);
DOM.addClass(tab.label, 'active'); DOM.addClass(tab.label, 'active');
DOM.addClass(tab.header, 'active'); DOM.addClass(tab.header, 'active');
tab.header.setAttribute('aria-selected', 'true'); tab.header.setAttribute('aria-selected', 'true');
@@ -197,8 +205,8 @@ export class TabbedPanel extends Disposable {
if (!actualTab) { if (!actualTab) {
return; return;
} }
if (actualTab.view && actualTab.view.remove) { if (actualTab.tab.view && actualTab.tab.view.remove) {
actualTab.view.remove(); actualTab.tab.view.remove();
} }
if (actualTab.header && actualTab.header.remove) { if (actualTab.header && actualTab.header.remove) {
actualTab.header.remove(); actualTab.header.remove();
@@ -218,6 +226,9 @@ export class TabbedPanel extends Disposable {
} }
} }
} }
if (!this._shownTabId && this._tabMap.size > 0) {
this.showTab(map.values(this._tabMap)[0].tab.identifier);
}
} }
if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) { if (!this.options.showHeaderWhenSingleView && this._tabMap.size === 1 && this._headerVisible) {
@@ -302,7 +313,7 @@ export class TabbedPanel extends Disposable {
if (tab) { if (tab) {
tab.body.style.width = dimension.width + 'px'; tab.body.style.width = dimension.width + 'px';
tab.body.style.height = dimension.height + 'px'; tab.body.style.height = dimension.height + 'px';
tab.view.layout(dimension); tab.tab.view.layout(dimension);
} }
} }
} }
@@ -311,7 +322,7 @@ export class TabbedPanel extends Disposable {
if (this._shownTabId) { if (this._shownTabId) {
const tab = this._tabMap.get(this._shownTabId); const tab = this._tabMap.get(this._shownTabId);
if (tab) { if (tab) {
tab.view.focus(); tab.tab.view.focus();
} }
} }
} }

View File

@@ -181,7 +181,7 @@ export class QueryResultsView extends Disposable {
this.resultsTab = this._register(new ResultsTab(instantiationService)); this.resultsTab = this._register(new ResultsTab(instantiationService));
this.messagesTab = this._register(new MessagesTab(instantiationService)); this.messagesTab = this._register(new MessagesTab(instantiationService));
this.chartTab = this._register(new ChartTab(instantiationService)); this.chartTab = this._register(new ChartTab(instantiationService));
this._panelView = this._register(new TabbedPanel(container, { showHeaderWhenSingleView: false })); this._panelView = this._register(new TabbedPanel(container, { showHeaderWhenSingleView: true }));
this._register(attachTabbedPanelStyler(this._panelView, themeService)); this._register(attachTabbedPanelStyler(this._panelView, themeService));
this.qpTab = this._register(new QueryPlanTab()); this.qpTab = this._register(new QueryPlanTab());
this.topOperationsTab = this._register(new TopOperationsTab(instantiationService)); this.topOperationsTab = this._register(new TopOperationsTab(instantiationService));
@@ -195,17 +195,40 @@ export class QueryResultsView extends Disposable {
})); }));
} }
private hasResults(runner: QueryRunner): boolean {
let hasResults = false;
for (const batch of runner.batchSets) {
if (batch.resultSetSummaries.length > 0) {
hasResults = true;
break;
}
}
return hasResults;
}
private setQueryRunner(runner: QueryRunner) { private setQueryRunner(runner: QueryRunner) {
if (runner.hasCompleted && !this.hasResults(runner)) {
this.hideResults();
} else {
this.showResults();
}
this.resultsTab.queryRunner = runner; this.resultsTab.queryRunner = runner;
this.messagesTab.queryRunner = runner; this.messagesTab.queryRunner = runner;
this.chartTab.queryRunner = runner; this.chartTab.queryRunner = runner;
this.runnerDisposables.push(runner.onQueryStart(e => { this.runnerDisposables.push(runner.onQueryStart(e => {
this.showResults();
this.hideChart(); this.hideChart();
this.hidePlan(); this.hidePlan();
this.hideDynamicViewModelTabs(); this.hideDynamicViewModelTabs();
this.input.state.visibleTabs = new Set(); this.input.state.visibleTabs = new Set();
this.input.state.activeTab = this.resultsTab.identifier; this.input.state.activeTab = this.resultsTab.identifier;
})); }));
this.runnerDisposables.push(runner.onQueryEnd(() => {
if (!this.hasResults(runner)) {
this.hideResults();
}
}));
if (this.input.state.visibleTabs.has(this.chartTab.identifier) && !this._panelView.contains(this.chartTab)) { if (this.input.state.visibleTabs.has(this.chartTab.identifier) && !this._panelView.contains(this.chartTab)) {
this._panelView.pushTab(this.chartTab); this._panelView.pushTab(this.chartTab);
} else if (!this.input.state.visibleTabs.has(this.chartTab.identifier) && this._panelView.contains(this.chartTab)) { } else if (!this.input.state.visibleTabs.has(this.chartTab.identifier) && this._panelView.contains(this.chartTab)) {
@@ -311,6 +334,19 @@ export class QueryResultsView extends Disposable {
} }
} }
public hideResults() {
if (this._panelView.contains(this.resultsTab)) {
this._panelView.removeTab(this.resultsTab.identifier);
}
}
public showResults() {
if (!this._panelView.contains(this.resultsTab)) {
this._panelView.pushTab(this.resultsTab, 0);
}
this._panelView.showTab(this.resultsTab.identifier);
}
public showPlan(xml: string) { public showPlan(xml: string) {
this.input.state.visibleTabs.add(this.qpTab.identifier); this.input.state.visibleTabs.add(this.qpTab.identifier);
if (!this._panelView.contains(this.qpTab)) { if (!this._panelView.contains(this.qpTab)) {