mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Markdown Horizontal Scrollbar Fix (#17083)
* dynamically change horizontal scrollbar * working horizontal scrollbar * created new event to handle both scrollbar and mouse wheel * only show scrollbar when needed
This commit is contained in:
@@ -37,6 +37,7 @@ export class QueryTextEditor extends BaseTextEditor {
|
|||||||
private _hideLineNumbers: boolean;
|
private _hideLineNumbers: boolean;
|
||||||
private _scrollbarHeight: number;
|
private _scrollbarHeight: number;
|
||||||
private _lineHeight: number;
|
private _lineHeight: number;
|
||||||
|
private _shouldAddHorizontalScrollbarHeight: boolean = false;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@ITelemetryService telemetryService: ITelemetryService,
|
@ITelemetryService telemetryService: ITelemetryService,
|
||||||
@@ -123,6 +124,10 @@ export class QueryTextEditor extends BaseTextEditor {
|
|||||||
return editorWidget.getScrollHeight();
|
return editorWidget.getScrollHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get shouldAddHorizontalScrollbar(): boolean {
|
||||||
|
return this._shouldAddHorizontalScrollbarHeight;
|
||||||
|
}
|
||||||
|
|
||||||
public setHeightToScrollHeight(configChanged?: boolean, isEditorCollapsed?: boolean,) {
|
public setHeightToScrollHeight(configChanged?: boolean, isEditorCollapsed?: boolean,) {
|
||||||
let editorWidget = this.getControl() as ICodeEditor;
|
let editorWidget = this.getControl() as ICodeEditor;
|
||||||
let layoutInfo = editorWidget.getLayoutInfo();
|
let layoutInfo = editorWidget.getLayoutInfo();
|
||||||
@@ -146,7 +151,7 @@ export class QueryTextEditor extends BaseTextEditor {
|
|||||||
// number of lines that wrap). Finally, viewportColumn is calculated on editor resizing automatically; we can use it to ensure
|
// number of lines that wrap). Finally, viewportColumn is calculated on editor resizing automatically; we can use it to ensure
|
||||||
// that the viewportColumn will always be greater than any character's column in an editor.
|
// that the viewportColumn will always be greater than any character's column in an editor.
|
||||||
let numberWrappedLines = 0;
|
let numberWrappedLines = 0;
|
||||||
let shouldAddHorizontalScrollbarHeight = false;
|
this._shouldAddHorizontalScrollbarHeight = false;
|
||||||
if (!this._lineHeight || configChanged) {
|
if (!this._lineHeight || configChanged) {
|
||||||
this._lineHeight = editorWidget.getOption(EditorOption.lineHeight) || 18;
|
this._lineHeight = editorWidget.getOption(EditorOption.lineHeight) || 18;
|
||||||
}
|
}
|
||||||
@@ -162,14 +167,14 @@ export class QueryTextEditor extends BaseTextEditor {
|
|||||||
for (let line = 1; line <= lineCount; line++) {
|
for (let line = 1; line <= lineCount; line++) {
|
||||||
// The horizontal scrollbar always appears 1 column past the viewport column when word wrap is disabled
|
// The horizontal scrollbar always appears 1 column past the viewport column when word wrap is disabled
|
||||||
if (editorWidgetModel.getLineMaxColumn(line) >= layoutInfo.viewportColumn + 1) {
|
if (editorWidgetModel.getLineMaxColumn(line) >= layoutInfo.viewportColumn + 1) {
|
||||||
shouldAddHorizontalScrollbarHeight = true;
|
this._shouldAddHorizontalScrollbarHeight = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let editorHeightUsingLines = this._lineHeight * (lineCount + numberWrappedLines);
|
let editorHeightUsingLines = this._lineHeight * (lineCount + numberWrappedLines);
|
||||||
let editorHeightUsingMinHeight = Math.max(Math.min(editorHeightUsingLines, this._maxHeight), this._minHeight);
|
let editorHeightUsingMinHeight = Math.max(Math.min(editorHeightUsingLines, this._maxHeight), this._minHeight);
|
||||||
editorHeightUsingMinHeight = shouldAddHorizontalScrollbarHeight ? editorHeightUsingMinHeight + this._scrollbarHeight : editorHeightUsingMinHeight;
|
editorHeightUsingMinHeight = this._shouldAddHorizontalScrollbarHeight ? editorHeightUsingMinHeight + this._scrollbarHeight : editorHeightUsingMinHeight;
|
||||||
this.setHeight(editorHeightUsingMinHeight);
|
this.setHeight(editorHeightUsingMinHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,6 +242,8 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
|
|||||||
|
|
||||||
this.onContentChanged.emit();
|
this.onContentChanged.emit();
|
||||||
this.checkForLanguageMagics();
|
this.checkForLanguageMagics();
|
||||||
|
// When content is updated we have to also update the horizontal scrollbar
|
||||||
|
this.horizontalScrollbar();
|
||||||
}));
|
}));
|
||||||
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
this._register(this._configurationService.onDidChangeConfiguration(e => {
|
||||||
if (e.affectsConfiguration('editor.wordWrap') || e.affectsConfiguration('editor.fontSize')) {
|
if (e.affectsConfiguration('editor.wordWrap') || e.affectsConfiguration('editor.fontSize')) {
|
||||||
@@ -249,6 +251,9 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
this._register(this.model.layoutChanged(() => this._layoutEmitter.fire(), this));
|
this._register(this.model.layoutChanged(() => this._layoutEmitter.fire(), this));
|
||||||
|
// Handles mouse wheel and scrollbar events
|
||||||
|
this._register(Event.debounce(this.model.onScroll.event, (l, e) => e, 250, /*leading=*/false)
|
||||||
|
(() => this.horizontalScrollbar()));
|
||||||
this._register(this.cellModel.onExecutionStateChange(event => {
|
this._register(this.cellModel.onExecutionStateChange(event => {
|
||||||
if (event === CellExecutionState.Running && !this.cellModel.stdInVisible) {
|
if (event === CellExecutionState.Running && !this.cellModel.stdInVisible) {
|
||||||
this.setFocusAndScroll();
|
this.setFocusAndScroll();
|
||||||
@@ -264,7 +269,6 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
|
|||||||
}
|
}
|
||||||
this._layoutEmitter.fire();
|
this._layoutEmitter.fire();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.layout();
|
this.layout();
|
||||||
|
|
||||||
if (this._cellModel.isCollapsed) {
|
if (this._cellModel.isCollapsed) {
|
||||||
@@ -277,6 +281,51 @@ export class CodeComponent extends CellView implements OnInit, OnChanges {
|
|||||||
DOM.getContentWidth(this.codeElement.nativeElement),
|
DOM.getContentWidth(this.codeElement.nativeElement),
|
||||||
DOM.getContentHeight(this.codeElement.nativeElement)));
|
DOM.getContentHeight(this.codeElement.nativeElement)));
|
||||||
this._editor.setHeightToScrollHeight(false, this._cellModel.isCollapsed);
|
this._editor.setHeightToScrollHeight(false, this._cellModel.isCollapsed);
|
||||||
|
this.horizontalScrollbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Horizontal Scrollbar function will ensure we only calculate and trigger this if word wrap is off and it is a markdown cell
|
||||||
|
* This will adjust the horizontal scrollbar to either a fixed position at the bottom of the viewport (visible area)
|
||||||
|
* or it will set it to the bottom of the markdown editor if it is in the viewport (visible area)
|
||||||
|
*/
|
||||||
|
public horizontalScrollbar(): void {
|
||||||
|
let showScrollbar: boolean = this._editor.shouldAddHorizontalScrollbar;
|
||||||
|
let horizontalScrollbar: HTMLElement = this.codeElement.nativeElement.querySelector('div.scrollbar.horizontal');
|
||||||
|
if (this._configurationService.getValue('editor.wordWrap') === 'off' && this.cellModel.cellType !== CellTypes.Code && this.cellModel.source.length > 0 && showScrollbar) {
|
||||||
|
// Get markdown split view horizontal scrollbar
|
||||||
|
let viewport: HTMLElement = document.querySelector('.scrollable');
|
||||||
|
let markdownEditor: HTMLElement = this.codeElement.nativeElement.closest('.show-markdown .editor');
|
||||||
|
|
||||||
|
//Get values based on current context of the editor and ADS window
|
||||||
|
let markdownEditorBottom = Math.floor(markdownEditor.getBoundingClientRect().bottom);
|
||||||
|
let viewportBottom = Math.floor(viewport.getBoundingClientRect().bottom);
|
||||||
|
let viewportHeight = DOM.getTotalHeight(viewport);
|
||||||
|
let viewportTop = Math.floor(document.querySelector('.scrollable').getBoundingClientRect().top);
|
||||||
|
|
||||||
|
// Have to offset the height based on the contents viewport and the additional scrollbars that are present in markdown editor and notebook
|
||||||
|
let horizontalTop = Math.floor(Math.abs(viewportTop + viewportHeight) - Math.abs(2 * horizontalScrollbar.scrollHeight));
|
||||||
|
|
||||||
|
// Set opacity for both fixed and absolute
|
||||||
|
horizontalScrollbar.style.opacity = '1';
|
||||||
|
|
||||||
|
// If the bottom of the editor is in the viewport, then set the horizontal scrollbar to the bottom of the editor space
|
||||||
|
if (markdownEditorBottom < viewportBottom) {
|
||||||
|
horizontalScrollbar.style.position = 'absolute';
|
||||||
|
horizontalScrollbar.style.left = '0px';
|
||||||
|
horizontalScrollbar.style.top = '';
|
||||||
|
horizontalScrollbar.style.bottom = '0px';
|
||||||
|
// If the bottom of the editor is not in the viewport, then set the horizontal scrollbar to the bottom of the viewport
|
||||||
|
} else {
|
||||||
|
horizontalScrollbar.style.position = 'fixed';
|
||||||
|
horizontalScrollbar.style.left = '';
|
||||||
|
horizontalScrollbar.style.top = horizontalTop + 'px';
|
||||||
|
horizontalScrollbar.style.bottom = '';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If horizontal scrollbar is not needed then set do not show it
|
||||||
|
horizontalScrollbar.style.opacity = '0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected initActionBar() {
|
protected initActionBar() {
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ import { CellToolbarComponent } from 'sql/workbench/contrib/notebook/browser/cel
|
|||||||
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
import { NotebookViewsExtension } from 'sql/workbench/services/notebook/browser/notebookViews/notebookViewsExtension';
|
||||||
import { MaskedLabeledMenuItemActionItem } from 'sql/platform/actions/browser/menuEntryActionViewItem';
|
import { MaskedLabeledMenuItemActionItem } from 'sql/platform/actions/browser/menuEntryActionViewItem';
|
||||||
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||||
|
import { Emitter } from 'vs/base/common/event';
|
||||||
|
|
||||||
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
export const NOTEBOOK_SELECTOR: string = 'notebook-component';
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
private navigationResult: nb.NavigationResult;
|
private navigationResult: nb.NavigationResult;
|
||||||
public previewFeaturesEnabled: boolean = false;
|
public previewFeaturesEnabled: boolean = false;
|
||||||
public doubleClickEditEnabled: boolean;
|
public doubleClickEditEnabled: boolean;
|
||||||
|
private _onScroll = new Emitter<void>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
@Inject(forwardRef(() => ChangeDetectorRef)) private _changeRef: ChangeDetectorRef,
|
||||||
@@ -223,6 +225,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
//Saves scrollTop value on scroll change
|
//Saves scrollTop value on scroll change
|
||||||
public scrollHandler(event: Event) {
|
public scrollHandler(event: Event) {
|
||||||
this._scrollTop = (<HTMLElement>event.srcElement).scrollTop;
|
this._scrollTop = (<HTMLElement>event.srcElement).scrollTop;
|
||||||
|
this.model.onScroll.fire();
|
||||||
}
|
}
|
||||||
|
|
||||||
public unselectActiveCell() {
|
public unselectActiveCell() {
|
||||||
@@ -339,6 +342,7 @@ export class NotebookComponent extends AngularDisposable implements OnInit, OnDe
|
|||||||
this._register(this._model.kernelChanged((kernelArgs) => this.handleKernelChanged(kernelArgs)));
|
this._register(this._model.kernelChanged((kernelArgs) => this.handleKernelChanged(kernelArgs)));
|
||||||
this._register(this._model.onCellTypeChanged(() => this.detectChanges()));
|
this._register(this._model.onCellTypeChanged(() => this.detectChanges()));
|
||||||
this._register(this._model.layoutChanged(() => this.detectChanges()));
|
this._register(this._model.layoutChanged(() => this.detectChanges()));
|
||||||
|
this._register(this.model.onScroll.event(() => this._onScroll.fire()));
|
||||||
|
|
||||||
this.setLoading(false);
|
this.setLoading(false);
|
||||||
// Check if URI fragment is present; if it is, navigate to section by default
|
// Check if URI fragment is present; if it is, navigate to section by default
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
private _trustedMode: boolean;
|
private _trustedMode: boolean;
|
||||||
private _onActiveCellChanged = new Emitter<ICellModel | undefined>();
|
private _onActiveCellChanged = new Emitter<ICellModel | undefined>();
|
||||||
private _onCellTypeChanged = new Emitter<ICellModel>();
|
private _onCellTypeChanged = new Emitter<ICellModel>();
|
||||||
|
private _onScrollEmitter = new Emitter<void>();
|
||||||
|
|
||||||
private _cells: ICellModel[] | undefined;
|
private _cells: ICellModel[] | undefined;
|
||||||
private _defaultLanguageInfo: nb.ILanguageInfo | undefined;
|
private _defaultLanguageInfo: nb.ILanguageInfo | undefined;
|
||||||
@@ -213,6 +214,10 @@ export class NotebookModel extends Disposable implements INotebookModel {
|
|||||||
return this._contextsChangedEmitter.event;
|
return this._contextsChangedEmitter.event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get onScroll(): Emitter<void> {
|
||||||
|
return this._onScrollEmitter;
|
||||||
|
}
|
||||||
|
|
||||||
public get contextsLoading(): Event<void> {
|
public get contextsLoading(): Event<void> {
|
||||||
return this._contextsLoadingEmitter.event;
|
return this._contextsLoadingEmitter.event;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user