Skip to content

Commit

Permalink
aux window - make it easier to create status elements in all opened w…
Browse files Browse the repository at this point in the history
…indows (#208012)
  • Loading branch information
bpasero committed Mar 19, 2024
1 parent 55d6f30 commit 7c4c34e
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 131 deletions.
46 changes: 46 additions & 0 deletions src/vs/workbench/browser/parts/statusbar/statusbarPart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,9 @@ export class StatusbarService extends MultiWindowParts<StatusbarPart> implements

readonly mainPart = this._register(this.instantiationService.createInstance(MainStatusbarPart));

private readonly _onDidCreateAuxiliaryStatusbarPart = this._register(new Emitter<AuxiliaryStatusbarPart>());
private readonly onDidCreateAuxiliaryStatusbarPart = this._onDidCreateAuxiliaryStatusbarPart.event;

constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IStorageService storageService: IStorageService,
Expand All @@ -688,6 +691,8 @@ export class StatusbarService extends MultiWindowParts<StatusbarPart> implements
//#region Auxiliary Statusbar Parts

createAuxiliaryStatusbarPart(container: HTMLElement): IAuxiliaryStatusbarPart {

// Container
const statusbarPartContainer = document.createElement('footer');
statusbarPartContainer.classList.add('part', 'statusbar');
statusbarPartContainer.setAttribute('role', 'status');
Expand All @@ -696,13 +701,17 @@ export class StatusbarService extends MultiWindowParts<StatusbarPart> implements
statusbarPartContainer.setAttribute('tabindex', '0');
container.appendChild(statusbarPartContainer);

// Statusbar Part
const statusbarPart = this.instantiationService.createInstance(AuxiliaryStatusbarPart, statusbarPartContainer);
const disposable = this.registerPart(statusbarPart);

statusbarPart.create(statusbarPartContainer);

Event.once(statusbarPart.onWillDispose)(() => disposable.dispose());

// Emit internal event
this._onDidCreateAuxiliaryStatusbarPart.fire(statusbarPart);

return statusbarPart;
}

Expand All @@ -717,9 +726,46 @@ export class StatusbarService extends MultiWindowParts<StatusbarPart> implements
readonly onDidChangeEntryVisibility = this.mainPart.onDidChangeEntryVisibility;

addEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor {
if (entry.showInAllWindows) {
return this.doAddEntryToAllWindows(entry, id, alignment, priorityOrLocation);
}

return this.mainPart.addEntry(entry, id, alignment, priorityOrLocation);
}

private doAddEntryToAllWindows(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor {
const entryDisposables = new DisposableStore();

const accessors = new Set<IStatusbarEntryAccessor>();

function addEntry(part: StatusbarPart | AuxiliaryStatusbarPart): void {
const partDisposables = new DisposableStore();
partDisposables.add(part.onWillDispose(() => partDisposables.dispose()));

const accessor = partDisposables.add(part.addEntry(entry, id, alignment, priorityOrLocation));
accessors.add(accessor);
partDisposables.add(toDisposable(() => accessors.delete(accessor)));

entryDisposables.add(partDisposables);
partDisposables.add(toDisposable(() => entryDisposables.delete(partDisposables)));
}

for (const part of this.parts) {
addEntry(part);
}

entryDisposables.add(this.onDidCreateAuxiliaryStatusbarPart(part => addEntry(part)));

return {
update: (entry: IStatusbarEntry) => {
for (const update of accessors) {
update.update(entry);
}
},
dispose: () => entryDisposables.dispose()
};
}

isEntryVisible(id: string): boolean {
return this.mainPart.isEntryVisible(id);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import Severity from 'vs/base/common/severity';
import { localize } from 'vs/nls';
Expand All @@ -13,75 +13,30 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur
import { INotificationHandle, INotificationService, NotificationPriority } from 'vs/platform/notification/common/notification';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';

class ScreenReaderModeStatusEntry extends Disposable {

private readonly screenReaderModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());

constructor(@IStatusbarService private readonly statusbarService: IStatusbarService) {
super();
}

updateScreenReaderModeElement(visible: boolean): void {
if (visible) {
if (!this.screenReaderModeElement.value) {
const text = localize('screenReaderDetected', "Screen Reader Optimized");
this.screenReaderModeElement.value = this.statusbarService.addEntry({
name: localize('status.editor.screenReaderMode', "Screen Reader Mode"),
text,
ariaLabel: text,
command: 'showEditorScreenReaderNotification',
kind: 'prominent'
}, 'status.editor.screenReaderMode', StatusbarAlignment.RIGHT, 100.6);
}
} else {
this.screenReaderModeElement.clear();
}
}
}

export class AccessibilityStatus extends Disposable implements IWorkbenchContribution {

static readonly ID = 'workbench.contrib.accessibilityStatus';

private screenReaderNotification: INotificationHandle | null = null;
private promptedScreenReader: boolean = false;
private readonly screenReaderModeElements = new Set<ScreenReaderModeStatusEntry>();
private readonly screenReaderModeElement = this._register(new MutableDisposable<IStatusbarEntryAccessor>());

constructor(
@IConfigurationService private readonly configurationService: IConfigurationService,
@INotificationService private readonly notificationService: INotificationService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@IInstantiationService instantiationService: IInstantiationService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService
@IStatusbarService private readonly statusbarService: IStatusbarService
) {
super();

this.createScreenReaderModeElement(instantiationService, this._store);
this.updateScreenReaderModeElements(accessibilityService.isScreenReaderOptimized());
this._register(CommandsRegistry.registerCommand({ id: 'showEditorScreenReaderNotification', handler: () => this.showScreenReaderNotification() }));

CommandsRegistry.registerCommand({ id: 'showEditorScreenReaderNotification', handler: () => this.showScreenReaderNotification() });
this.updateScreenReaderModeElement(this.accessibilityService.isScreenReaderOptimized());

this.registerListeners();
}

private createScreenReaderModeElement(instantiationService: IInstantiationService, disposables: DisposableStore): ScreenReaderModeStatusEntry {
const entry = disposables.add(instantiationService.createInstance(ScreenReaderModeStatusEntry));

this.screenReaderModeElements.add(entry);
disposables.add(toDisposable(() => this.screenReaderModeElements.delete(entry)));

return entry;
}

private updateScreenReaderModeElements(visible: boolean): void {
for (const entry of this.screenReaderModeElements) {
entry.updateScreenReaderModeElement(visible);
}
}

private registerListeners(): void {
this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this.onScreenReaderModeChange()));

Expand All @@ -90,11 +45,6 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib
this.onScreenReaderModeChange();
}
}));

this._register(this.editorGroupService.onDidCreateAuxiliaryEditorPart(({ instantiationService, disposables }) => {
const entry = this.createScreenReaderModeElement(instantiationService, disposables);
entry.updateScreenReaderModeElement(this.accessibilityService.isScreenReaderOptimized());
}));
}

private showScreenReaderNotification(): void {
Expand All @@ -120,6 +70,23 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib

Event.once(this.screenReaderNotification.onDidClose)(() => this.screenReaderNotification = null);
}
private updateScreenReaderModeElement(visible: boolean): void {
if (visible) {
if (!this.screenReaderModeElement.value) {
const text = localize('screenReaderDetected', "Screen Reader Optimized");
this.screenReaderModeElement.value = this.statusbarService.addEntry({
name: localize('status.editor.screenReaderMode', "Screen Reader Mode"),
text,
ariaLabel: text,
command: 'showEditorScreenReaderNotification',
kind: 'prominent',
showInAllWindows: true
}, 'status.editor.screenReaderMode', StatusbarAlignment.RIGHT, 100.6);
}
} else {
this.screenReaderModeElement.clear();
}
}

private onScreenReaderModeChange(): void {

Expand All @@ -138,14 +105,6 @@ export class AccessibilityStatus extends Disposable implements IWorkbenchContrib
if (this.screenReaderNotification) {
this.screenReaderNotification.close();
}
this.updateScreenReaderModeElements(this.accessibilityService.isScreenReaderOptimized());
}

override dispose(): void {
super.dispose();

for (const entry of this.screenReaderModeElements) {
entry.dispose();
}
this.updateScreenReaderModeElement(this.accessibilityService.isScreenReaderOptimized());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { IConfigurationRegistry, Extensions } from 'vs/platform/configuration/common/configurationRegistry';
import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IHostService } from 'vs/workbench/services/host/browser/host';
import { getCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
Expand Down Expand Up @@ -909,7 +908,6 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe
@ISpeechService private readonly speechService: ISpeechService,
@IConfigurationService private readonly configurationService: IConfigurationService,
@ICommandService private readonly commandService: ICommandService,
@IEditorGroupsService private readonly editorGroupService: IEditorGroupsService,
@IInstantiationService instantiationService: IInstantiationService,
@IEditorService private readonly editorService: IEditorService,
@IHostService private readonly hostService: IHostService,
Expand Down Expand Up @@ -938,10 +936,6 @@ export class KeywordActivationContribution extends Disposable implements IWorkbe
this.handleKeywordActivation();
}
}));

this._register(this.editorGroupService.onDidCreateAuxiliaryEditorPart(({ instantiationService, disposables }) => {
disposables.add(instantiationService.createInstance(KeywordActivationStatusEntry));
}));
}

private updateConfiguration(): void {
Expand Down Expand Up @@ -1108,7 +1102,8 @@ class KeywordActivationStatusEntry extends Disposable {
tooltip: this.speechService.hasActiveKeywordRecognition ? KeywordActivationStatusEntry.STATUS_ACTIVE : KeywordActivationStatusEntry.STATUS_INACTIVE,
ariaLabel: this.speechService.hasActiveKeywordRecognition ? KeywordActivationStatusEntry.STATUS_ACTIVE : KeywordActivationStatusEntry.STATUS_INACTIVE,
command: KeywordActivationStatusEntry.STATUS_COMMAND,
kind: 'prominent'
kind: 'prominent',
showInAllWindows: true
};
}

Expand Down
80 changes: 20 additions & 60 deletions src/vs/workbench/contrib/scrollLocking/browser/scrollLocking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,49 +3,18 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle';
import { Disposable, DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle';
import { ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { localize, localize2 } from 'vs/nls';
import { Categories } from 'vs/platform/action/common/actionCommonCategories';
import { Action2, registerAction2 } from 'vs/platform/actions/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IEditorPane, IEditorPaneScrollPosition, isEditorPaneWithScrolling } from 'vs/workbench/common/editor';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStatusbarEntryAccessor, IStatusbarService, StatusbarAlignment } from 'vs/workbench/services/statusbar/browser/statusbar';

class SyncScrollStatusEntry extends Disposable {

private readonly syncScrollEntry = this._register(new MutableDisposable<IStatusbarEntryAccessor>());

constructor(@IStatusbarService private readonly statusbarService: IStatusbarService) {
super();
}

updateSyncScroll(visible: boolean): void {
if (visible) {
if (!this.syncScrollEntry.value) {
this.syncScrollEntry.value = this.statusbarService.addEntry({
name: 'Scrolling Locked',
text: 'Scrolling Locked',
tooltip: 'Lock Scrolling enabled',
ariaLabel: 'Scrolling Locked',
command: {
id: 'workbench.action.toggleLockedScrolling',
title: ''
},
kind: 'prominent'
}, 'status.scrollLockingEnabled', StatusbarAlignment.RIGHT, 102);
}
} else {
this.syncScrollEntry.clear();
}
}
}

export class SyncScroll extends Disposable implements IWorkbenchContribution {

static readonly ID = 'workbench.contrib.syncScrolling';
Expand All @@ -55,14 +24,13 @@ export class SyncScroll extends Disposable implements IWorkbenchContribution {
private readonly syncScrollDispoasbles = this._register(new DisposableStore());
private readonly paneDisposables = new DisposableStore();

private statusBarEntries = new Set<SyncScrollStatusEntry>();
private statusBarEntry = this._register(new MutableDisposable<IStatusbarEntryAccessor>());

private isActive: boolean = false;

constructor(
@IEditorService private readonly editorService: IEditorService,
@IEditorGroupsService private readonly editorGroupsService: IEditorGroupsService,
@IInstantiationService private readonly instantiationService: IInstantiationService
@IStatusbarService private readonly statusbarService: IStatusbarService
) {
super();

Expand Down Expand Up @@ -175,36 +143,29 @@ export class SyncScroll extends Disposable implements IWorkbenchContribution {

// Actions & Commands

private createStatusBarItem(instantiationService: IInstantiationService, disposables: DisposableStore): SyncScrollStatusEntry {
const entry = disposables.add(instantiationService.createInstance(SyncScrollStatusEntry));

this.statusBarEntries.add(entry);
disposables.add(toDisposable(() => this.statusBarEntries.delete(entry)));

return entry;
}

private registerStatusBarItems() {
const entry = this.createStatusBarItem(this.instantiationService, this._store);
entry.updateSyncScroll(this.isActive);

this._register(this.editorGroupsService.onDidCreateAuxiliaryEditorPart(({ instantiationService, disposables }) => {
const entry = this.createStatusBarItem(instantiationService, disposables);
entry.updateSyncScroll(this.isActive);
}));
}

private toggleStatusbarItem(active: boolean): void {
for (const item of this.statusBarEntries) {
item.updateSyncScroll(active);
if (active) {
if (!this.statusBarEntry.value) {
this.statusBarEntry.value = this.statusbarService.addEntry({
name: 'Scrolling Locked',
text: 'Scrolling Locked',
tooltip: 'Lock Scrolling enabled',
ariaLabel: 'Scrolling Locked',
command: {
id: 'workbench.action.toggleLockedScrolling',
title: ''
},
kind: 'prominent',
showInAllWindows: true
}, 'status.scrollLockingEnabled', StatusbarAlignment.RIGHT, 102);
}
} else {
this.statusBarEntry.clear();
}
}

private registerActions() {
const $this = this;

this.registerStatusBarItems();

this._register(registerAction2(class extends Action2 {
constructor() {
super({
Expand Down Expand Up @@ -253,7 +214,6 @@ export class SyncScroll extends Disposable implements IWorkbenchContribution {
}

override dispose(): void {
this.statusBarEntries.forEach(entry => entry.dispose());
this.deactivate();
super.dispose();
}
Expand Down

0 comments on commit 7c4c34e

Please sign in to comment.