From 10f5bb396d151d586f5d9ecc23a345970eb7ac1b Mon Sep 17 00:00:00 2001 From: Brian Lambert Date: Mon, 16 Sep 2024 09:59:49 -0700 Subject: [PATCH] If it's OK to do so, drive focus into the newly-created CodeEditorWidget (#4685) --- .../browser/components/consoleInput.tsx | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/src/vs/workbench/contrib/positronConsole/browser/components/consoleInput.tsx b/src/vs/workbench/contrib/positronConsole/browser/components/consoleInput.tsx index 12ccfeb41b5..dd3258848f5 100644 --- a/src/vs/workbench/contrib/positronConsole/browser/components/consoleInput.tsx +++ b/src/vs/workbench/contrib/positronConsole/browser/components/consoleInput.tsx @@ -8,7 +8,7 @@ import 'vs/css!./consoleInput'; // React. import * as React from 'react'; -import { FocusEvent, useEffect, useRef } from 'react'; // eslint-disable-line no-duplicate-imports +import { FocusEvent, useEffect, useLayoutEffect, useRef } from 'react'; // eslint-disable-line no-duplicate-imports // Other dependencies. import * as DOM from 'vs/base/browser/dom'; @@ -90,6 +90,40 @@ export const ConsoleInput = (props: ConsoleInputProps) => { const [, setCurrentCodeFragment, currentCodeFragmentRef] = useStateRef(undefined); + /** + * Determines whether it is OK to take focus. + * @returns true if it is OK to take focus; otherwise, false. + */ + const okToTakeFocus = () => { + // https://github.com/posit-dev/positron/issues/2802 + // It's only OK to take focus if there is no focused editor. This avoids stealing focus when + // the user could be actively working in an editor. + + // Get the context key service context. + const contextKeyContext = positronConsoleContext.contextKeyService.getContext( + DOM.getActiveElement() + ); + + // Sensitive to all editor contexts, simple (e.g. git commit textbox) or not (e.g. code + // editor). + if (contextKeyContext.getValue(EditorContextKeys.textInputFocus.key)) { + return false; + } + + // Sensitive to all quick pick contexts, e.g. the commande palette or the file picker. + if (contextKeyContext.getValue(InQuickPickContextKey.key)) { + return false; + } + + // Sensitive to terminal focus. + if (contextKeyContext.getValue(TerminalContextKeys.focus.key)) { + return false; + } + + // It's OK to take focus. + return true; + }; + /** * Updates the code editor widget position. * @param linePosition The line position. @@ -556,8 +590,8 @@ export const ConsoleInput = (props: ConsoleInputProps) => { } }; - // Main useEffect hook. - useEffect(() => { + // Main useLayoutEffect hook. + useLayoutEffect(() => { // Create the disposable store for cleanup. const disposableStore = new DisposableStore(); @@ -758,20 +792,8 @@ export const ConsoleInput = (props: ConsoleInputProps) => { positronConsoleContext.positronConsoleService.onDidChangeActivePositronConsoleInstance( positronConsoleInstance => { if (positronConsoleInstance === props.positronConsoleInstance) { - // https://github.com/posit-dev/positron/issues/2802 - // Only take focus if there is no focused editor to avoid stealing - // focus when the user could be actively working in an editor - - const ctxt = positronConsoleContext.contextKeyService.getContext(DOM.getActiveElement()); - - // Sensitive to all editor contexts, simple (e.g. git commit textbox) or not (e.g. code editor) - const inTextInput = ctxt.getValue(EditorContextKeys.textInputFocus.key); - // Sensitive to all quick pick contexts, e.g. the commande palette or the file picker const inQuickPick = positronConsoleContext.contextKeyService.getContextKeyValue(InQuickPickContextKey.key); - const inQuickPick = ctxt.getValue(InQuickPickContextKey.key); - // Sensitive to terminal focus - const inTerminal = ctxt.getValue(TerminalContextKeys.focus.key); - - if (!inTextInput && !inQuickPick && !inTerminal) { + // If it's OK to take focus, drive focus into the code editor widget. + if (okToTakeFocus()) { codeEditorWidget.focus(); } } @@ -880,6 +902,11 @@ export const ConsoleInput = (props: ConsoleInputProps) => { }) ); + // If it's OK to take focus, drive focus into the code editor widget. + if (okToTakeFocus()) { + codeEditorWidget.focus(); + } + // Return the cleanup function that will dispose of the disposables. return () => disposableStore.dispose(); // eslint-disable-next-line react-hooks/exhaustive-deps