Save edit data scroll position when switching tabs (#2129)

This commit is contained in:
Matt Irvine
2018-08-03 16:18:18 -07:00
committed by GitHub
parent df804d0729
commit 424eb90dd8
12 changed files with 157 additions and 39 deletions

View File

@@ -16,6 +16,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import Severity from 'vs/base/common/severity';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
/**
* Input for the EditDataEditor.
@@ -37,6 +38,8 @@ export class EditDataInput extends EditorInput implements IConnectableInput {
private _css: HTMLStyleElement;
private _useQueryFilter: boolean;
public savedViewState: IEditorViewState;
constructor(
private _uri: URI,
private _schemaName,

View File

@@ -8,6 +8,7 @@
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event';
/**
* Input for the EditDataResultsEditor. This input helps with logic for the viewing and editing of
@@ -25,6 +26,9 @@ export class EditDataResultsInput extends EditorInput {
private _editorContainer: HTMLElement;
public css: HTMLStyleElement;
public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>();
constructor(private _uri: string) {
super();
this._visible = false;

View File

@@ -13,7 +13,7 @@ import { Builder } from 'vs/base/browser/builder';
import { EditorOptions, EditorInput } from 'vs/workbench/common/editor';
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
import { Position, IEditorControl, IEditor } from 'vs/platform/editor/common/editor';
import { Position, IEditorControl, IEditor, IEditorInput } from 'vs/platform/editor/common/editor';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IThemeService } from 'vs/platform/theme/common/themeService';
@@ -43,6 +43,8 @@ import { IFlexibleSash, VerticalFlexibleSash, HorizontalFlexibleSash } from 'sql
import { Orientation } from 'vs/base/browser/ui/sash/sash';
import { EditDataResultsEditor } from 'sql/parts/editData/editor/editDataResultsEditor';
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { Emitter } from 'vs/base/common/event';
/**
* Editor that hosts an action bar and a resultSetInput for an edit data session
@@ -96,6 +98,14 @@ export class EditDataEditor extends BaseEditor {
if (contextKeyService) {
this._queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
}
if (_editorGroupService) {
_editorGroupService.onEditorOpening(e => {
if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) {
this.saveEditorViewState();
}
});
}
}
// PUBLIC METHODS ////////////////////////////////////////////////////////////
@@ -594,7 +604,15 @@ export class EditDataEditor extends BaseEditor {
// Run all three steps synchronously
return createEditors()
.then(onEditorsCreated)
.then(doLayout);
.then(doLayout)
.then(() => {
if (newInput.results) {
newInput.results.onRestoreViewStateEmitter.fire();
}
if (newInput.savedViewState) {
this._sqlEditor.getControl().restoreViewState(newInput.savedViewState);
}
});
}
private _setSashDimension(): void {
@@ -631,7 +649,6 @@ export class EditDataEditor extends BaseEditor {
* has been opened with the same editor, or we are opening the editor for the first time).
*/
private _updateInput(oldInput: EditDataInput, newInput: EditDataInput, options?: EditorOptions): TPromise<void> {
if (this._sqlEditor) {
this._sqlEditor.clearInput();
}
@@ -727,4 +744,16 @@ export class EditDataEditor extends BaseEditor {
public queryPaneEnabled(): boolean {
return this.editDataInput.queryPaneEnabled;
}
private saveEditorViewState(): void {
let editDataInput = this.input as EditDataInput;
if (editDataInput) {
if (this._sqlEditor) {
editDataInput.savedViewState = this._sqlEditor.getControl().saveViewState();
}
if (editDataInput.results) {
editDataInput.results.onSaveViewStateEmitter.fire();
}
}
}
}

View File

@@ -23,6 +23,7 @@ import { IEditDataComponentParams } from 'sql/services/bootstrap/bootstrapParams
import { EditDataModule } from 'sql/parts/grid/views/editData/editData.module';
import { EDITDATA_SELECTOR } from 'sql/parts/grid/views/editData/editData.component';
import { EditDataResultsInput } from 'sql/parts/editData/common/editDataResultsInput';
import { Event } from 'vs/base/common/event';
export class EditDataResultsEditor extends BaseEditor {
@@ -109,7 +110,11 @@ export class EditDataResultsEditor extends BaseEditor {
// Otherwise many components will be left around and be subscribed
// to events from the backing data service
const parent = input.container;
let params: IEditDataComponentParams = { dataService: dataService };
let params: IEditDataComponentParams = {
dataService: dataService,
onSaveViewState: input.onSaveViewStateEmitter.event,
onRestoreViewState: input.onRestoreViewStateEmitter.event
};
bootstrapAngular(this._instantiationService,
EditDataModule,
parent,

View File

@@ -13,7 +13,7 @@ import 'vs/css!sql/parts/grid/media/slickGrid';
import 'vs/css!./media/editData';
import { ElementRef, ChangeDetectorRef, OnInit, OnDestroy, Component, Inject, forwardRef, EventEmitter } from '@angular/core';
import { IGridDataRow, VirtualizedCollection } from 'angular2-slickgrid';
import { IGridDataRow, VirtualizedCollection, ISlickRange } from 'angular2-slickgrid';
import { IGridDataSet } from 'sql/parts/grid/common/interfaces';
import * as Services from 'sql/parts/grid/services/sharedServices';
@@ -81,6 +81,12 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
public overrideCellFn: (rowNumber, columnId, value?, data?) => string;
public loadDataFunction: (offset: number, count: number) => Promise<IGridDataRow[]>;
private savedViewState: {
gridSelections: ISlickRange[];
scrollTop;
scrollLeft;
};
constructor(
@Inject(forwardRef(() => ElementRef)) el: ElementRef,
@Inject(forwardRef(() => ChangeDetectorRef)) cd: ChangeDetectorRef,
@@ -98,6 +104,8 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
this._el.nativeElement.className = 'slickgridContainer';
this.dataService = params.dataService;
this.actionProvider = this.instantiationService.createInstance(EditDataGridActionProvider, this.dataService, this.onGridSelectAll(), this.onDeleteRow(), this.onRevertRow());
params.onRestoreViewState(() => this.restoreViewState());
params.onSaveViewState(() => this.saveViewState());
}
/**
@@ -578,4 +586,25 @@ export class EditDataComponent extends GridParentComponent implements OnInit, On
? (this._defaultNumShowingRows + 1) * this._rowHeight + 10
: this.getMaxHeight(rowCount);
}
private saveViewState(): void {
let gridSelections = this.slickgrids.toArray()[0].getSelectedRanges();
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
this.savedViewState = {
gridSelections,
scrollTop: viewport.scrollTop,
scrollLeft: viewport.scrollLeft
};
}
private restoreViewState(): void {
if (this.savedViewState) {
this.slickgrids.toArray()[0].selection = this.savedViewState.gridSelections;
let viewport = ((this.slickgrids.toArray()[0] as any)._grid.getCanvasNode() as HTMLElement).parentElement;
viewport.scrollLeft = this.savedViewState.scrollLeft;
viewport.scrollTop = this.savedViewState.scrollTop;
this.savedViewState = undefined;
}
}
}

View File

@@ -27,6 +27,12 @@ import { Event, Emitter } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { Dimension } from 'vs/base/browser/dom';
import { textFormatter } from 'sql/parts/grid/services/sharedServices';
import { IEditorInput } from 'vs/platform/editor/common/editor';
export interface ProfilerTableViewState {
scrollTop: number;
scrollLeft: number;
}
export class ProfilerTableEditor extends BaseEditor implements IProfilerController, ITableController {
@@ -219,4 +225,18 @@ export class ProfilerTableEditor extends BaseEditor implements IProfilerControll
this._findState.changeMatchInfo(0, 0, undefined);
}
}
public saveViewState(): ProfilerTableViewState {
let viewElement = this._profilerTable.grid.getCanvasNode().parentElement;
return {
scrollTop: viewElement.scrollTop,
scrollLeft: viewElement.scrollLeft
};
}
public restoreViewState(state: ProfilerTableViewState): void {
let viewElement = this._profilerTable.grid.getCanvasNode().parentElement;
viewElement.scrollTop = state.scrollTop;
viewElement.scrollLeft = state.scrollLeft;
}
}

View File

@@ -12,7 +12,7 @@ import { IProfilerService, IProfilerViewTemplate } from 'sql/parts/profiler/serv
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
import { attachTableStyler } from 'sql/common/theme/styler';
import { IProfilerStateChangedEvent } from './profilerState';
import { ProfilerTableEditor } from './controller/profilerTableEditor';
import { ProfilerTableEditor, ProfilerTableViewState } from './controller/profilerTableEditor';
import * as Actions from 'sql/parts/profiler/contrib/profilerActions';
import { CONTEXT_PROFILER_EDITOR, PROFILER_TABLE_COMMAND_SEARCH } from './interfaces';
import { SelectBox } from 'sql/base/browser/ui/selectBox/selectBox';
@@ -45,6 +45,7 @@ import { CommonFindController, FindStartFocusAction } from 'vs/editor/contrib/fi
import * as types from 'vs/base/common/types';
import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler';
import { DARK, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
class BasicView extends View {
private _previousSize: number;
@@ -131,6 +132,7 @@ export class ProfilerEditor extends BaseEditor {
private _createAction: Actions.ProfilerCreate;
private _collapsedPanelAction: Actions.ProfilerCollapsablePanelAction;
private _savedTableViewStates = new Map<ProfilerInput, ProfilerTableViewState>();
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -140,10 +142,19 @@ export class ProfilerEditor extends BaseEditor {
@IModelService private _modelService: IModelService,
@IProfilerService private _profilerService: IProfilerService,
@IContextKeyService private _contextKeyService: IContextKeyService,
@IContextViewService private _contextViewService: IContextViewService
@IContextViewService private _contextViewService: IContextViewService,
@IEditorGroupService private _editorGroupService: IEditorGroupService
) {
super(ProfilerEditor.ID, telemetryService, themeService);
this._profilerEditorContextKey = CONTEXT_PROFILER_EDITOR.bindTo(this._contextKeyService);
if (_editorGroupService) {
_editorGroupService.onEditorOpening(e => {
if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) {
this.saveEditorViewState();
}
});
}
}
protected createEditor(parent: HTMLElement): void {
@@ -366,8 +377,13 @@ export class ProfilerEditor extends BaseEditor {
}
public setInput(input: ProfilerInput, options?: EditorOptions): TPromise<void> {
let savedViewState = this._savedTableViewStates.get(input);
this._profilerEditorContextKey.set(true);
if (input instanceof ProfilerInput && input.matches(this.input)) {
if (savedViewState) {
this._profilerTableEditor.restoreViewState(savedViewState);
}
return TPromise.as(null);
}
@@ -398,6 +414,9 @@ export class ProfilerEditor extends BaseEditor {
this._profilerTableEditor.updateState();
this._splitView.layout();
this._profilerTableEditor.focus();
if (savedViewState) {
this._profilerTableEditor.restoreViewState(savedViewState);
}
});
}
@@ -498,6 +517,12 @@ export class ProfilerEditor extends BaseEditor {
this._body.style.height = (dimension.height - (28 + 4)) + 'px';
this._splitView.layout(dimension.height - (28 + 4));
}
private saveEditorViewState(): void {
if (this.input && this._profilerTableEditor) {
this._savedTableViewStates.set(this.input, this._profilerTableEditor.saveViewState());
}
}
}
abstract class SettingsCommand extends Command {

View File

@@ -11,6 +11,7 @@ import URI from 'vs/base/common/uri';
import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput';
import { EditorInput, EditorModel, ConfirmResult, EncodingMode, IEncodingSupport } from 'vs/workbench/common/editor';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEditorViewState } from 'vs/editor/common/editorCommon';
import { IConnectionManagementService, IConnectableInput, INewConnectionParams, RunQueryOnConnectionMode } from 'sql/parts/connection/common/connectionManagement';
import { QueryResultsInput } from 'sql/parts/query/common/queryResultsInput';
@@ -56,6 +57,8 @@ export class QueryInput extends EditorInput implements IEncodingSupport, IConnec
private _toDispose: IDisposable[];
private _currentEventCallbacks: IDisposable[];
public savedViewState: IEditorViewState;
constructor(
private _description: string,
private _sql: UntitledEditorInput,

View File

@@ -8,6 +8,7 @@
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { EditorInput } from 'vs/workbench/common/editor';
import { Emitter } from 'vs/base/common/event';
/**
* Input for the QueryResultsEditor. This input helps with logic for the viewing and editing of
@@ -25,6 +26,9 @@ export class QueryResultsInput extends EditorInput {
private _editorContainer: HTMLElement;
public css: HTMLStyleElement;
public readonly onRestoreViewStateEmitter = new Emitter<void>();
public readonly onSaveViewStateEmitter = new Emitter<void>();
constructor(private _uri: string) {
super();
this._visible = false;

View File

@@ -89,9 +89,6 @@ export class QueryEditor extends BaseEditor {
private _estimatedQueryPlanAction: EstimatedQueryPlanAction;
private _actualQueryPlanAction: ActualQueryPlanAction;
private _savedViewStates = new Map<IEditorInput, IEditorViewState>();
private _resultViewStateChangeEmitters = new Map<QueryResultsInput, { onSaveViewState: Emitter<void>; onRestoreViewState: Emitter<void> }>();
constructor(
@ITelemetryService _telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService,
@@ -111,6 +108,14 @@ export class QueryEditor extends BaseEditor {
if (contextKeyService) {
this.queryEditorVisible = queryContext.QueryEditorVisibleContext.bindTo(contextKeyService);
}
if (_editorGroupService) {
_editorGroupService.onEditorOpening(e => {
if (this.isVisible() && (e.input !== this.input || e.position !== this.position)) {
this.saveEditorViewState();
}
});
}
}
// PROPERTIES //////////////////////////////////////////////////////////
@@ -500,19 +505,11 @@ export class QueryEditor extends BaseEditor {
* Handles setting input for this editor.
*/
private _updateInput(oldInput: QueryInput, newInput: QueryInput, options?: EditorOptions): TPromise<void> {
if (this._sqlEditor) {
let sqlEditorViewState = this._sqlEditor.getControl().saveViewState();
this._savedViewStates.set(this._sqlEditor.input, sqlEditorViewState);
this._sqlEditor.clearInput();
}
if (oldInput) {
let resultViewStateChangeEmitters = this._resultViewStateChangeEmitters.get(oldInput.results);
if (resultViewStateChangeEmitters) {
resultViewStateChangeEmitters.onSaveViewState.fire();
}
this._disposeEditors();
}
@@ -587,11 +584,11 @@ export class QueryEditor extends BaseEditor {
.then(onEditorsCreated)
.then(doLayout)
.then(() => {
if (this._resultViewStateChangeEmitters.has(newInput.results)) {
this._resultViewStateChangeEmitters.get(newInput.results).onRestoreViewState.fire();
if (newInput.results) {
newInput.results.onRestoreViewStateEmitter.fire();
}
if (this._savedViewStates.has(newInput.sql)) {
this._sqlEditor.getControl().restoreViewState(this._savedViewStates.get(newInput.sql));
if (newInput.savedViewState) {
this._sqlEditor.getControl().restoreViewState(newInput.savedViewState);
}
});
}
@@ -624,14 +621,6 @@ export class QueryEditor extends BaseEditor {
*/
private _onResultsEditorCreated(resultsEditor: QueryResultsEditor, resultsInput: QueryResultsInput, options: EditorOptions): TPromise<void> {
this._resultsEditor = resultsEditor;
if (!this._resultViewStateChangeEmitters.has(resultsInput)) {
this._resultViewStateChangeEmitters.set(resultsInput, {
onRestoreViewState: new Emitter<void>(),
onSaveViewState: new Emitter<void>()
});
}
let emitters = this._resultViewStateChangeEmitters.get(resultsInput);
this._resultsEditor.setViewStateChangeEvents(emitters.onRestoreViewState.event, emitters.onSaveViewState.event);
return this._resultsEditor.setInput(resultsInput, options);
}
@@ -891,6 +880,18 @@ export class QueryEditor extends BaseEditor {
editor.focus();
}
private saveEditorViewState(): void {
let queryInput = this.input as QueryInput;
if (queryInput) {
if (this._sqlEditor) {
queryInput.savedViewState = this._sqlEditor.getControl().saveViewState();
}
if (queryInput.results) {
queryInput.results.onSaveViewStateEmitter.fire();
}
}
}
// TESTING PROPERTIES ////////////////////////////////////////////////////////////
public get resultsEditor(): QueryResultsEditor {

View File

@@ -96,8 +96,6 @@ export class QueryResultsEditor extends BaseEditor {
public static AngularSelectorString: string = 'slickgrid-container.slickgridContainer';
protected _rawOptions: BareResultsGridInfo;
protected _input: QueryResultsInput;
private _restoreViewStateEvent: Event<void>;
private _saveViewStateEvent: Event<void>;
constructor(
@ITelemetryService telemetryService: ITelemetryService,
@@ -152,11 +150,6 @@ export class QueryResultsEditor extends BaseEditor {
return TPromise.wrap<void>(null);
}
public setViewStateChangeEvents(onRestoreViewStateEvent: Event<void>, onSaveViewStateEvent: Event<void>) {
this._restoreViewStateEvent = onRestoreViewStateEvent;
this._saveViewStateEvent = onSaveViewStateEvent;
}
/**
* Load the angular components and record for this input that we have done so
*/
@@ -179,8 +172,8 @@ export class QueryResultsEditor extends BaseEditor {
// to events from the backing data service
let params: IQueryComponentParams = {
dataService: dataService,
onSaveViewState: this._saveViewStateEvent,
onRestoreViewState: this._restoreViewStateEvent
onSaveViewState: this.input.onSaveViewStateEmitter.event,
onRestoreViewState: this.input.onRestoreViewStateEmitter.event
};
bootstrapAngular(this._instantiationService,
QueryOutputModule,

View File

@@ -18,6 +18,8 @@ export interface IQueryComponentParams extends IBootstrapParams {
export interface IEditDataComponentParams extends IBootstrapParams {
dataService: DataService;
onSaveViewState: Event<void>;
onRestoreViewState: Event<void>;
}
export interface IDefaultComponentParams extends IBootstrapParams {