diff --git a/news/2 Fixes/7851.md b/news/2 Fixes/7851.md new file mode 100644 index 000000000000..d45e641a6d22 --- /dev/null +++ b/news/2 Fixes/7851.md @@ -0,0 +1 @@ +Hide the parameters intellisense widget in the `Notebook Editor` when it is not longer required. diff --git a/src/datascience-ui/react-common/monacoEditor.tsx b/src/datascience-ui/react-common/monacoEditor.tsx index 35f5c1c778f4..4c2d19915c8f 100644 --- a/src/datascience-ui/react-common/monacoEditor.tsx +++ b/src/datascience-ui/react-common/monacoEditor.tsx @@ -60,6 +60,15 @@ export class MonacoEditor extends React.Component { + this.hideParameterWidget(); + })); + // Track focus changes to make sure we update our widget parent and widget position this.subscriptions.push(editor.onDidFocusEditorWidget(() => { this.throttledUpdateWidgetPosition(); @@ -192,7 +206,10 @@ export class MonacoEditor extends React.Component this.hideParameterWidget(), 100); } } + /** + * This will hide the parameter widget if the user is not hovering over + * the parameter widget for this monaco editor. + * + * Notes: See issue https://github.com/microsoft/vscode-python/issues/7851 for further info. + * Hide the parameter widget if all of the following conditions have been met: + * - ditor doesn't have focus + * - Mouse is not over the editor + * - Mouse is not over (hovering) the parameter widget + * + * @private + * @returns + * @memberof MonacoEditor + */ + private hideParameterWidget(){ + if (!this.state.editor || !this.state.editor.getDomNode() || !this.widgetParent){ + return; + } + // Find all elements that the user is hovering over. + // Its possible the parameter widget is one of them. + const hoverElements: Element[] = Array.prototype.slice.call(document.querySelectorAll(':hover')); + // Find all parameter widgets related to this monaco editor that are currently displayed. + const visibleParameterHintsWidgets: Element[] = Array.prototype.slice.call(this.widgetParent.querySelectorAll('.parameter-hints-widget.visible')); + if (hoverElements.length === 0 && visibleParameterHintsWidgets.length === 0){ + // If user is not hovering over anything and there are no visible parameter widgets, + // then, we have nothing to do but get out of here. + return; + } + + // Find all parameter widgets related to this monaco editor. + const knownParameterHintsWidgets: HTMLDivElement[] = Array.prototype.slice.call(this.widgetParent.querySelectorAll('.parameter-hints-widget')); + + // Lets not assume we'll have the exact same DOM for parameter widgets. + // So, just remove the event handler, and add it again later. + if (this.parameterWidget){ + this.parameterWidget.removeEventListener('mouseleave', this.outermostParentLeave); + } + // These are the classes that will appear on a parameter widget when they are visible. + const parameterWidgetClasses = ['editor-widget', 'parameter-hints-widget', 'visible']; + + // Find the parameter widget the user is currently hovering over. + this.parameterWidget = hoverElements.find(item => { + if (!item.className) { + return false; + } + // Check if user is hovering over a parameter widget. + const classes = item.className.split(' '); + if (!parameterWidgetClasses.every(cls => classes.indexOf(cls) >= 0)){ + // Not all classes required in a parameter hint widget are in this element. + // Hence this is not a parameter widget. + return false; + } + + // Ok, this element that the user is hovering over is a parameter widget. + + // Next, check whether this parameter widget belongs to this monaco editor. + // We have a list of parameter widgets that belong to this editor, hence a simple lookup. + return knownParameterHintsWidgets.some(widget => widget === item); + }); + + if (this.parameterWidget){ + // We know the user is hovering over the parameter widget for this editor. + // Hovering could mean the user is scrolling through a large parameter list. + // We need to add a mouse leave event handler, so as to hide this. + this.parameterWidget.addEventListener('mouseleave', this.outermostParentLeave); + + // In case the event handler doesn't get fired, have a backup of checking within 1s. + setTimeout(() => this.hideParameterWidget(), 1000); + return; + } + if (visibleParameterHintsWidgets.length === 0){ + // There are no parameter widgets displayed for this editor. + // Hence nothing to do. + return; + } + // If the editor has focus, don't hide the parameter widget. + // This is the default behavior. Let the user hit `Escape` or click somewhere + // to forcefully hide the parameter widget. + if (this.state.editor.hasWidgetFocus()) { + return; + } + + // If we got here, then the user is not hovering over the paramter widgets. + // & the editor doesn't have focus. + // However some of the parameter widgets associated with this monaco editor are visible. + // We need to hide them. + + // Solution: Hide the widgets manually. + knownParameterHintsWidgets.forEach(widget => { + widget.setAttribute('class', widget.className.split(' ').filter(cls => cls !== 'visible').join(' ')); + if (widget.style.visibility !== 'hidden') { + widget.style.visibility = 'hidden'; + } + }); + } + private updateMargin(editor: monacoEditor.editor.IStandaloneCodeEditor) { const editorNode = editor.getDomNode(); if (editorNode) {