Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit fa75cf0

Browse files
authored
Hide the parameters list when editor doesn't have focus (#7987)
* Added comments
1 parent dbabb6f commit fa75cf0

File tree

2 files changed

+118
-1
lines changed

2 files changed

+118
-1
lines changed

news/2 Fixes/7851.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Hide the parameters intellisense widget in the `Notebook Editor` when it is not longer required.

src/datascience-ui/react-common/monacoEditor.tsx

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,15 @@ export class MonacoEditor extends React.Component<IMonacoEditorProps, IMonacoEdi
6060
private throttledUpdateWidgetPosition = throttle(this.updateWidgetPosition.bind(this), 100);
6161
private monacoContainer : HTMLDivElement | undefined;
6262

63+
/**
64+
* Reference to parameter widget (used by monaco to display parameter docs).
65+
*
66+
* @private
67+
* @type {Element}
68+
* @memberof MonacoEditor
69+
*/
70+
private parameterWidget?: Element;
71+
6372
constructor(props: IMonacoEditorProps) {
6473
super(props);
6574
this.state = { editor: undefined, model: null, visibleLineCount: -1, attached: false, widgetsReparented: false };
@@ -157,6 +166,11 @@ export class MonacoEditor extends React.Component<IMonacoEditorProps, IMonacoEdi
157166
}
158167
}));
159168

169+
// When editor loses focus, hide parameter widgets (if any currently displayed).
170+
this.subscriptions.push(editor.onDidBlurEditorWidget(() => {
171+
this.hideParameterWidget();
172+
}));
173+
160174
// Track focus changes to make sure we update our widget parent and widget position
161175
this.subscriptions.push(editor.onDidFocusEditorWidget(() => {
162176
this.throttledUpdateWidgetPosition();
@@ -192,7 +206,10 @@ export class MonacoEditor extends React.Component<IMonacoEditorProps, IMonacoEdi
192206
if (window) {
193207
window.removeEventListener('resize', this.windowResized);
194208
}
195-
209+
if (this.parameterWidget){
210+
this.parameterWidget.removeEventListener('mouseleave', this.outermostParentLeave);
211+
this.parameterWidget = undefined;
212+
}
196213
if (this.outermostParent) {
197214
this.outermostParent.removeEventListener('mouseleave', this.outermostParentLeave);
198215
this.outermostParent = null;
@@ -434,9 +451,108 @@ export class MonacoEditor extends React.Component<IMonacoEditorProps, IMonacoEdi
434451
if (this.state.editor && !this.enteredHover) {
435452
// If we haven't already entered hover, then act like it shuts down
436453
this.onHoverLeave();
454+
// Possible user is viewing the parameter hints, wait before user moves the mouse.
455+
// Waiting for 1s is too long to move the mouse and hide the hints (100ms seems like a good fit).
456+
setTimeout(() => this.hideParameterWidget(), 100);
437457
}
438458
}
439459

460+
/**
461+
* This will hide the parameter widget if the user is not hovering over
462+
* the parameter widget for this monaco editor.
463+
*
464+
* Notes: See issue https://github.com/microsoft/vscode-python/issues/7851 for further info.
465+
* Hide the parameter widget if all of the following conditions have been met:
466+
* - ditor doesn't have focus
467+
* - Mouse is not over the editor
468+
* - Mouse is not over (hovering) the parameter widget
469+
*
470+
* @private
471+
* @returns
472+
* @memberof MonacoEditor
473+
*/
474+
private hideParameterWidget(){
475+
if (!this.state.editor || !this.state.editor.getDomNode() || !this.widgetParent){
476+
return;
477+
}
478+
// Find all elements that the user is hovering over.
479+
// Its possible the parameter widget is one of them.
480+
const hoverElements: Element[] = Array.prototype.slice.call(document.querySelectorAll(':hover'));
481+
// Find all parameter widgets related to this monaco editor that are currently displayed.
482+
const visibleParameterHintsWidgets: Element[] = Array.prototype.slice.call(this.widgetParent.querySelectorAll('.parameter-hints-widget.visible'));
483+
if (hoverElements.length === 0 && visibleParameterHintsWidgets.length === 0){
484+
// If user is not hovering over anything and there are no visible parameter widgets,
485+
// then, we have nothing to do but get out of here.
486+
return;
487+
}
488+
489+
// Find all parameter widgets related to this monaco editor.
490+
const knownParameterHintsWidgets: HTMLDivElement[] = Array.prototype.slice.call(this.widgetParent.querySelectorAll('.parameter-hints-widget'));
491+
492+
// Lets not assume we'll have the exact same DOM for parameter widgets.
493+
// So, just remove the event handler, and add it again later.
494+
if (this.parameterWidget){
495+
this.parameterWidget.removeEventListener('mouseleave', this.outermostParentLeave);
496+
}
497+
// These are the classes that will appear on a parameter widget when they are visible.
498+
const parameterWidgetClasses = ['editor-widget', 'parameter-hints-widget', 'visible'];
499+
500+
// Find the parameter widget the user is currently hovering over.
501+
this.parameterWidget = hoverElements.find(item => {
502+
if (!item.className) {
503+
return false;
504+
}
505+
// Check if user is hovering over a parameter widget.
506+
const classes = item.className.split(' ');
507+
if (!parameterWidgetClasses.every(cls => classes.indexOf(cls) >= 0)){
508+
// Not all classes required in a parameter hint widget are in this element.
509+
// Hence this is not a parameter widget.
510+
return false;
511+
}
512+
513+
// Ok, this element that the user is hovering over is a parameter widget.
514+
515+
// Next, check whether this parameter widget belongs to this monaco editor.
516+
// We have a list of parameter widgets that belong to this editor, hence a simple lookup.
517+
return knownParameterHintsWidgets.some(widget => widget === item);
518+
});
519+
520+
if (this.parameterWidget){
521+
// We know the user is hovering over the parameter widget for this editor.
522+
// Hovering could mean the user is scrolling through a large parameter list.
523+
// We need to add a mouse leave event handler, so as to hide this.
524+
this.parameterWidget.addEventListener('mouseleave', this.outermostParentLeave);
525+
526+
// In case the event handler doesn't get fired, have a backup of checking within 1s.
527+
setTimeout(() => this.hideParameterWidget(), 1000);
528+
return;
529+
}
530+
if (visibleParameterHintsWidgets.length === 0){
531+
// There are no parameter widgets displayed for this editor.
532+
// Hence nothing to do.
533+
return;
534+
}
535+
// If the editor has focus, don't hide the parameter widget.
536+
// This is the default behavior. Let the user hit `Escape` or click somewhere
537+
// to forcefully hide the parameter widget.
538+
if (this.state.editor.hasWidgetFocus()) {
539+
return;
540+
}
541+
542+
// If we got here, then the user is not hovering over the paramter widgets.
543+
// & the editor doesn't have focus.
544+
// However some of the parameter widgets associated with this monaco editor are visible.
545+
// We need to hide them.
546+
547+
// Solution: Hide the widgets manually.
548+
knownParameterHintsWidgets.forEach(widget => {
549+
widget.setAttribute('class', widget.className.split(' ').filter(cls => cls !== 'visible').join(' '));
550+
if (widget.style.visibility !== 'hidden') {
551+
widget.style.visibility = 'hidden';
552+
}
553+
});
554+
}
555+
440556
private updateMargin(editor: monacoEditor.editor.IStandaloneCodeEditor) {
441557
const editorNode = editor.getDomNode();
442558
if (editorNode) {

0 commit comments

Comments
 (0)