0" class="results vertBox scrollable"
- (onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
- (focusin)="onGridFocus()" (focusout)="onGridFocusout()">
+
0" class="results vertBox scrollable"
+ (onScroll)="onScroll($event)" [scrollEnabled]="scrollEnabled" [class.hidden]="!resultActive"
+ (focusin)="onGridFocus()" (focusout)="onGridFocusout()">
= new EventEmitter();
public goToNextQueryOutputTabRequested: EventEmitter = new EventEmitter();
+ private savedViewState: {
+ gridSelections: ISlickRange[][];
+ resultsScroll: number;
+ messagePaneScroll: number;
+ slickGridScrolls: { vertical: number; horizontal: number }[];
+ };
+
@Input() public queryParameters: IQueryComponentParams;
@ViewChildren('slickgrid') slickgrids: QueryList;
@@ -168,6 +175,8 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
@ViewChild('resultsPane', { read: ElementRef }) private _resultsPane: ElementRef;
@ViewChild('queryLink', { read: ElementRef }) private _queryLinkElement: ElementRef;
@ViewChild('messagesContainer', { read: ElementRef }) private _messagesContainer: ElementRef;
+ @ViewChild('resultsScrollBox', { read: ElementRef }) private _resultsScrollBox: ElementRef;
+ @ViewChildren('slickgrid', { read: ElementRef }) private _slickgridElements: QueryList;
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@@ -225,6 +234,10 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
self._cd.detectChanges();
});
+
+ this.queryParameters.onSaveViewState(() => this.saveViewState());
+ this.queryParameters.onRestoreViewState(() => this.restoreViewState());
+
this.dataService.onAngularLoaded();
}
@@ -651,6 +664,43 @@ export class QueryComponent extends GridParentComponent implements OnInit, OnDes
}
}
+ private saveViewState(): void {
+ let gridSelections = this.slickgrids.map(grid => grid.getSelectedRanges());
+ let resultsScrollElement = (this._resultsScrollBox.nativeElement as HTMLElement);
+ let resultsScroll = resultsScrollElement.scrollTop;
+ let messagePaneScroll = (this._messagesContainer.nativeElement as HTMLElement).scrollTop;
+ let slickGridScrolls = this._slickgridElements.map(element => {
+ // Get the slick grid's viewport element and save its scroll position
+ let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
+ return {
+ vertical: scrollElement.scrollTop,
+ horizontal: scrollElement.scrollLeft
+ };
+ });
+
+ this.savedViewState = {
+ gridSelections,
+ messagePaneScroll,
+ resultsScroll,
+ slickGridScrolls
+ };
+ }
+
+ private restoreViewState(): void {
+ if (this.savedViewState) {
+ this.slickgrids.forEach((grid, index) => grid.selection = this.savedViewState.gridSelections[index]);
+ (this._resultsScrollBox.nativeElement as HTMLElement).scrollTop = this.savedViewState.resultsScroll;
+ (this._messagesContainer.nativeElement as HTMLElement).scrollTop = this.savedViewState.messagePaneScroll;
+ this._slickgridElements.forEach((element, index) => {
+ let scrollElement = (element.nativeElement as HTMLElement).children[0].children[3];
+ let savedScroll = this.savedViewState.slickGridScrolls[index];
+ scrollElement.scrollTop = savedScroll.vertical;
+ scrollElement.scrollLeft = savedScroll.horizontal;
+ });
+ this.savedViewState = undefined;
+ }
+ }
+
layout() {
this.resizeGrids();
}
diff --git a/src/sql/parts/query/editor/queryEditor.ts b/src/sql/parts/query/editor/queryEditor.ts
index 633dbddb06..8619622ff5 100644
--- a/src/sql/parts/query/editor/queryEditor.ts
+++ b/src/sql/parts/query/editor/queryEditor.ts
@@ -32,6 +32,7 @@ import { CodeEditor } from 'vs/editor/browser/codeEditor';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IRange } from 'vs/editor/common/core/range';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
+import { Emitter } from 'vs/base/common/event';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
import { QueryInput } from 'sql/parts/query/common/queryInput';
@@ -90,6 +91,7 @@ export class QueryEditor extends BaseEditor {
private _parseSyntaxAction: ParseSyntaxAction;
private _savedViewStates = new Map();
+ private _resultViewStateChangeEmitters = new Map; onRestoreViewState: Emitter }>();
constructor(
@ITelemetryService _telemetryService: ITelemetryService,
@@ -509,6 +511,11 @@ export class QueryEditor extends BaseEditor {
}
if (oldInput) {
+ let resultViewStateChangeEmitters = this._resultViewStateChangeEmitters.get(oldInput.results);
+ if (resultViewStateChangeEmitters) {
+ resultViewStateChangeEmitters.onSaveViewState.fire();
+ }
+
this._disposeEditors();
}
@@ -583,6 +590,9 @@ export class QueryEditor extends BaseEditor {
.then(onEditorsCreated)
.then(doLayout)
.then(() => {
+ if (this._resultViewStateChangeEmitters.has(newInput.results)) {
+ this._resultViewStateChangeEmitters.get(newInput.results).onRestoreViewState.fire();
+ }
if (this._savedViewStates.has(newInput.sql)) {
this._sqlEditor.getControl().restoreViewState(this._savedViewStates.get(newInput.sql));
}
@@ -617,6 +627,14 @@ export class QueryEditor extends BaseEditor {
*/
private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise {
this._resultsEditor = resultsEditor;
+ if (!this._resultViewStateChangeEmitters.has(resultsInput)) {
+ this._resultViewStateChangeEmitters.set(resultsInput, {
+ onRestoreViewState: new Emitter(),
+ onSaveViewState: new Emitter()
+ });
+ }
+ let emitters = this._resultViewStateChangeEmitters.get(resultsInput);
+ this._resultsEditor.setViewStateChangeEvents(emitters.onRestoreViewState.event, emitters.onSaveViewState.event);
return this._resultsEditor.setInput(resultsInput, options);
}
diff --git a/src/sql/parts/query/editor/queryResultsEditor.ts b/src/sql/parts/query/editor/queryResultsEditor.ts
index 34df4508bb..6c9af8ce54 100644
--- a/src/sql/parts/query/editor/queryResultsEditor.ts
+++ b/src/sql/parts/query/editor/queryResultsEditor.ts
@@ -26,6 +26,7 @@ import { IQueryComponentParams } from 'sql/services/bootstrap/bootstrapParams';
import { QueryOutputModule } from 'sql/parts/query/views/queryOutput.module';
import { QUERY_OUTPUT_SELECTOR } from 'sql/parts/query/views/queryOutput.component';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
+import { Event } from 'vs/base/common/event';
export const RESULTS_GRID_DEFAULTS = {
cellPadding: [6, 10, 5],
@@ -95,6 +96,8 @@ export class QueryResultsEditor extends BaseEditor {
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
protected _rawOptions: BareResultsGridInfo;
protected _input: QueryResultsInput;
+ private _restoreViewStateEvent: Event;
+ private _saveViewStateEvent: Event;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -149,6 +152,11 @@ export class QueryResultsEditor extends BaseEditor {
return TPromise.wrap(null);
}
+ public setViewStateChangeEvents(onRestoreViewStateEvent: Event, onSaveViewStateEvent: Event) {
+ this._restoreViewStateEvent = onRestoreViewStateEvent;
+ this._saveViewStateEvent = onSaveViewStateEvent;
+ }
+
/**
* Load the angular components and record for this input that we have done so
*/
@@ -169,7 +177,11 @@ export class QueryResultsEditor extends BaseEditor {
// Note: pass in input so on disposal this is cleaned up.
// Otherwise many components will be left around and be subscribed
// to events from the backing data service
- let params: IQueryComponentParams = { dataService: dataService };
+ let params: IQueryComponentParams = {
+ dataService: dataService,
+ onSaveViewState: this._saveViewStateEvent,
+ onRestoreViewState: this._restoreViewStateEvent
+ };
bootstrapAngular(this._instantiationService,
QueryOutputModule,
this.getContainer(),
diff --git a/src/sql/services/bootstrap/bootstrapParams.ts b/src/sql/services/bootstrap/bootstrapParams.ts
index d3f9d9521a..4c4f25b6fa 100644
--- a/src/sql/services/bootstrap/bootstrapParams.ts
+++ b/src/sql/services/bootstrap/bootstrapParams.ts
@@ -8,9 +8,12 @@ import { IConnectionProfile } from 'sql/parts/connection/common/interfaces';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { ConnectionContextKey } from 'sql/parts/connection/common/connectionContextKey';
import { IBootstrapParams } from './bootstrapService';
+import { Event } from 'vs/base/common/event';
export interface IQueryComponentParams extends IBootstrapParams {
dataService: DataService;
+ onSaveViewState: Event;
+ onRestoreViewState: Event;
}
export interface IEditDataComponentParams extends IBootstrapParams {