From 69d51866f0162632bbc11bd829faa4423b13ab21 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 23 May 2024 20:50:20 +0200 Subject: [PATCH 1/3] Initial implementation --- .../contrib/scm/browser/scm.contribution.ts | 25 ++++++++ .../contrib/scm/browser/scmViewPane.ts | 60 +++++++++++++++++++ 2 files changed, 85 insertions(+) diff --git a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts index 70892dfb7991a..9ffb901b97c1f 100644 --- a/src/vs/workbench/contrib/scm/browser/scm.contribution.ts +++ b/src/vs/workbench/contrib/scm/browser/scm.contribution.ts @@ -34,6 +34,7 @@ import { IQuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiff'; import { QuickDiffService } from 'vs/workbench/contrib/scm/common/quickDiffService'; import { getActiveElement } from 'vs/base/browser/dom'; import { SCMWorkingSetController } from 'vs/workbench/contrib/scm/browser/workingSet'; +import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; ModesRegistry.registerLanguage({ id: 'scminput', @@ -475,6 +476,30 @@ MenuRegistry.appendMenuItem(MenuId.SCMSourceControl, { when: ContextKeyExpr.and(ContextKeyExpr.equals('scmProviderHasRootUri', true), ContextKeyExpr.or(ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'integrated'), ContextKeyExpr.equals('config.terminal.sourceControlRepositoriesKind', 'both'))) }); +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.scm.action.focusPreviousResourceGroup', + weight: KeybindingWeight.WorkbenchContrib, + handler: async accessor => { + const viewsService = accessor.get(IViewsService); + const scmView = await viewsService.openView(VIEW_PANE_ID); + if (scmView) { + scmView.focusPreviousResourceGroup(); + } + } +}); + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: 'workbench.scm.action.focusNextResourceGroup', + weight: KeybindingWeight.WorkbenchContrib, + handler: async accessor => { + const viewsService = accessor.get(IViewsService); + const scmView = await viewsService.openView(VIEW_PANE_ID); + if (scmView) { + scmView.focusNextResourceGroup(); + } + } +}); + registerSingleton(ISCMService, SCMService, InstantiationType.Delayed); registerSingleton(ISCMViewService, SCMViewService, InstantiationType.Delayed); registerSingleton(IQuickDiffService, QuickDiffService, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 185aa10316fc5..8edf6da4a23cb 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -3466,6 +3466,66 @@ export class SCMViewPane extends ViewPane { }); } + focusPreviousResourceGroup(): void { + this.treeOperationSequencer.queue(async () => { + const getPreviousIndex = (index: number, length: number) => { + return index > 0 ? index - 1 : length - 1; + }; + + this.focusResourceGroup(getPreviousIndex); + }); + } + + focusNextResourceGroup(): void { + this.treeOperationSequencer.queue(async () => { + const getNextIndex = (index: number, length: number) => { + return index < length - 1 ? index + 1 : 0; + }; + + this.focusResourceGroup(getNextIndex); + }); + } + + private focusResourceGroup(getIndex: (index: number, length: number) => number): void { + if (!this.scmViewService.focusedRepository) { + return; + } + + let resourceGroupNext: ISCMResourceGroup | undefined; + + const resourceGroup = this.tree.getFocus().find(e => isSCMResourceGroup(e)); + const resourceGroups = this.scmViewService.focusedRepository.provider.groups; + const resourceGroupIndex = resourceGroup ? resourceGroups.indexOf(resourceGroup) : -1; + + if (resourceGroupIndex === -1) { + // First visible resource group + for (const resourceGroup of resourceGroups) { + if (this.tree.hasNode(resourceGroup)) { + resourceGroupNext = resourceGroup; + break; + } + } + } else { + // Next/Previous visible resource group + let index = getIndex(resourceGroupIndex, resourceGroups.length); + while (index !== resourceGroupIndex) { + if (this.tree.hasNode(resourceGroups[index])) { + resourceGroupNext = resourceGroups[index]; + break; + } + index = getIndex(index, resourceGroups.length); + } + } + + if (resourceGroupNext) { + this.tree.reveal(resourceGroupNext); + + this.tree.setSelection([resourceGroupNext]); + this.tree.setFocus([resourceGroupNext]); + this.tree.domFocus(); + } + } + override shouldShowWelcome(): boolean { return this.scmService.repositoryCount === 0; } From c07888e8c3b872410a8afe22e31dbd15d18e79ec Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 24 May 2024 08:34:37 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=92=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/scm/browser/scmViewPane.ts | 26 ++++++------------- 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 43bfdd12ae94a..6fdb6f8346c92 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -3468,27 +3468,16 @@ export class SCMViewPane extends ViewPane { } focusPreviousResourceGroup(): void { - this.treeOperationSequencer.queue(async () => { - const getPreviousIndex = (index: number, length: number) => { - return index > 0 ? index - 1 : length - 1; - }; - - this.focusResourceGroup(getPreviousIndex); - }); + this.treeOperationSequencer.queue(() => this.focusResourceGroup(-1)); } focusNextResourceGroup(): void { - this.treeOperationSequencer.queue(async () => { - const getNextIndex = (index: number, length: number) => { - return index < length - 1 ? index + 1 : 0; - }; - - this.focusResourceGroup(getNextIndex); - }); + this.treeOperationSequencer.queue(() => this.focusResourceGroup(1)); } - private focusResourceGroup(getIndex: (index: number, length: number) => number): void { - if (!this.scmViewService.focusedRepository) { + private async focusResourceGroup(delta: number): Promise { + if (!this.scmViewService.focusedRepository || + this.scmViewService.visibleRepositories.length === 0) { return; } @@ -3508,17 +3497,18 @@ export class SCMViewPane extends ViewPane { } } else { // Next/Previous visible resource group - let index = getIndex(resourceGroupIndex, resourceGroups.length); + let index = rot(resourceGroupIndex, resourceGroups.length); while (index !== resourceGroupIndex) { if (this.tree.hasNode(resourceGroups[index])) { resourceGroupNext = resourceGroups[index]; break; } - index = getIndex(index, resourceGroups.length); + index = rot(index, resourceGroups.length); } } if (resourceGroupNext) { + await this.tree.expandTo(resourceGroupNext); this.tree.reveal(resourceGroupNext); this.tree.setSelection([resourceGroupNext]); From ac3487d905a20866862e1dafb3921ca47441c78f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 24 May 2024 10:31:18 +0200 Subject: [PATCH 3/3] Fixed issue with focusing outside the tree --- .../contrib/scm/browser/scmViewPane.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 52ebda0622f7b..653d2cf3943eb 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -8,7 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { basename, dirname } from 'vs/base/common/resources'; import { IDisposable, Disposable, DisposableStore, combinedDisposable, dispose, toDisposable, MutableDisposable, DisposableMap } from 'vs/base/common/lifecycle'; import { ViewPane, IViewPaneOptions, ViewAction } from 'vs/workbench/browser/parts/views/viewPane'; -import { append, $, Dimension, asCSSUrl, trackFocus, clearNode, prepend, isPointerEvent } from 'vs/base/browser/dom'; +import { append, $, Dimension, asCSSUrl, trackFocus, clearNode, prepend, isPointerEvent, isActiveElement } from 'vs/base/browser/dom'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryProviderCacheEntry, SCMHistoryItemChangeTreeElement, SCMHistoryItemGroupTreeElement, SCMHistoryItemTreeElement, SCMViewSeparatorElement } from 'vs/workbench/contrib/scm/common/history'; import { ISCMResourceGroup, ISCMResource, InputValidationType, ISCMRepository, ISCMInput, IInputValidation, ISCMViewService, ISCMViewVisibleRepositoryChangeEvent, ISCMService, SCMInputChangeReason, VIEW_PANE_ID, ISCMActionButton, ISCMActionButtonDescriptor, ISCMRepositorySortKey, ISCMInputValueProviderContext, ISCMProvider } from 'vs/workbench/contrib/scm/common/scm'; @@ -3481,13 +3481,14 @@ export class SCMViewPane extends ViewPane { return; } - let resourceGroupNext: ISCMResourceGroup | undefined; - - const resourceGroup = this.tree.getFocus().find(e => isSCMResourceGroup(e)); + const treeHasDomFocus = isActiveElement(this.tree.getHTMLElement()); const resourceGroups = this.scmViewService.focusedRepository.provider.groups; - const resourceGroupIndex = resourceGroup ? resourceGroups.indexOf(resourceGroup) : -1; + const focusedResourceGroup = this.tree.getFocus().find(e => isSCMResourceGroup(e)); + const focusedResourceGroupIndex = treeHasDomFocus && focusedResourceGroup ? resourceGroups.indexOf(focusedResourceGroup) : -1; + + let resourceGroupNext: ISCMResourceGroup | undefined; - if (resourceGroupIndex === -1) { + if (focusedResourceGroupIndex === -1) { // First visible resource group for (const resourceGroup of resourceGroups) { if (this.tree.hasNode(resourceGroup)) { @@ -3497,13 +3498,13 @@ export class SCMViewPane extends ViewPane { } } else { // Next/Previous visible resource group - let index = rot(resourceGroupIndex, resourceGroups.length); - while (index !== resourceGroupIndex) { + let index = rot(focusedResourceGroupIndex + delta, resourceGroups.length); + while (index !== focusedResourceGroupIndex) { if (this.tree.hasNode(resourceGroups[index])) { resourceGroupNext = resourceGroups[index]; break; } - index = rot(index, resourceGroups.length); + index = rot(index + delta, resourceGroups.length); } }