Path: blob/main/src/vs/workbench/browser/parts/compositeBarActions.ts
3296 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import { localize } from '../../../nls.js';6import { Action, IAction, Separator } from '../../../base/common/actions.js';7import { $, addDisposableListener, append, clearNode, EventHelper, EventType, getDomNodePagePosition, hide, show } from '../../../base/browser/dom.js';8import { ICommandService } from '../../../platform/commands/common/commands.js';9import { toDisposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js';10import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js';11import { IThemeService, IColorTheme } from '../../../platform/theme/common/themeService.js';12import { NumberBadge, IBadge, IActivity, ProgressBadge, IconBadge } from '../../services/activity/common/activity.js';13import { IInstantiationService, ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js';14import { DelayedDragHandler } from '../../../base/browser/dnd.js';15import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js';16import { Emitter, Event } from '../../../base/common/event.js';17import { CompositeDragAndDropObserver, ICompositeDragAndDrop, Before2D, toggleDropEffect } from '../dnd.js';18import { Color } from '../../../base/common/color.js';19import { BaseActionViewItem, IActionViewItemOptions } from '../../../base/browser/ui/actionbar/actionViewItems.js';20import { Codicon } from '../../../base/common/codicons.js';21import { ThemeIcon } from '../../../base/common/themables.js';22import { IHoverService } from '../../../platform/hover/browser/hover.js';23import { IConfigurationService } from '../../../platform/configuration/common/configuration.js';24import { HoverPosition } from '../../../base/browser/ui/hover/hoverWidget.js';25import { URI } from '../../../base/common/uri.js';26import { badgeBackground, badgeForeground, contrastBorder } from '../../../platform/theme/common/colorRegistry.js';27import { Action2, IAction2Options } from '../../../platform/actions/common/actions.js';28import { ViewContainerLocation } from '../../common/views.js';29import { IPaneCompositePartService } from '../../services/panecomposite/browser/panecomposite.js';30import { createConfigureKeybindingAction } from '../../../platform/actions/common/menuService.js';3132export interface ICompositeBar {3334/**35* Unpins a composite from the composite bar.36*/37unpin(compositeId: string): void;3839/**40* Pin a composite inside the composite bar.41*/42pin(compositeId: string): void;4344/**45* Find out if a composite is pinned in the composite bar.46*/47isPinned(compositeId: string): boolean;4849/**50* Get pinned composite ids in the composite bar.51*/52getPinnedCompositeIds(): string[];5354/**55* Returns if badges are enabled for that specified composite.56* @param compositeId The id of the composite to check57*/58areBadgesEnabled(compositeId: string): boolean;5960/**61* Toggles whether or not badges are shown on that particular composite.62* @param compositeId The composite to toggle badge enablement for63*/64toggleBadgeEnablement(compositeId: string): void;6566/**67* Reorder composite ordering by moving a composite to the location of another composite.68*/69move(compositeId: string, tocompositeId: string): void;70}7172export interface ICompositeBarActionItem {73id: string;74name: string;75keybindingId?: string;76classNames?: string[];77iconUrl?: URI;78}7980export class CompositeBarAction extends Action {8182private readonly _onDidChangeCompositeBarActionItem = this._register(new Emitter<CompositeBarAction>());83readonly onDidChangeCompositeBarActionItem = this._onDidChangeCompositeBarActionItem.event;8485private readonly _onDidChangeActivity = this._register(new Emitter<IActivity[]>());86readonly onDidChangeActivity = this._onDidChangeActivity.event;8788private _activities: IActivity[] = [];8990constructor(private item: ICompositeBarActionItem) {91super(item.id, item.name, item.classNames?.join(' '), true);92}9394get compositeBarActionItem(): ICompositeBarActionItem {95return this.item;96}9798set compositeBarActionItem(item: ICompositeBarActionItem) {99this._label = item.name;100this.item = item;101this._onDidChangeCompositeBarActionItem.fire(this);102}103104get activities(): IActivity[] {105return this._activities;106}107108set activities(activities: IActivity[]) {109this._activities = activities;110this._onDidChangeActivity.fire(activities);111}112113activate(): void {114if (!this.checked) {115this._setChecked(true);116}117}118119deactivate(): void {120if (this.checked) {121this._setChecked(false);122}123}124125}126127export interface ICompositeBarColors {128readonly activeBackgroundColor?: Color;129readonly inactiveBackgroundColor?: Color;130readonly activeBorderColor?: Color;131readonly activeBackground?: Color;132readonly activeBorderBottomColor?: Color;133readonly activeForegroundColor?: Color;134readonly inactiveForegroundColor?: Color;135readonly badgeBackground?: Color;136readonly badgeForeground?: Color;137readonly dragAndDropBorder?: Color;138}139140export interface IActivityHoverOptions {141readonly position: () => HoverPosition;142}143144export interface ICompositeBarActionViewItemOptions extends IActionViewItemOptions {145readonly icon?: boolean;146readonly colors: (theme: IColorTheme) => ICompositeBarColors;147148readonly hoverOptions: IActivityHoverOptions;149readonly hasPopup?: boolean;150readonly compact?: boolean;151}152153export class CompositeBarActionViewItem extends BaseActionViewItem {154155protected container!: HTMLElement;156protected label!: HTMLElement;157protected badge!: HTMLElement;158protected override readonly options: ICompositeBarActionViewItemOptions;159160private badgeContent: HTMLElement | undefined;161private readonly badgeDisposable = this._register(new MutableDisposable<DisposableStore>());162private mouseUpTimeout: Timeout | undefined;163private keybindingLabel: string | undefined | null;164165constructor(166action: CompositeBarAction,167options: ICompositeBarActionViewItemOptions,168private readonly badgesEnabled: (compositeId: string) => boolean,169@IThemeService protected readonly themeService: IThemeService,170@IHoverService private readonly hoverService: IHoverService,171@IConfigurationService protected readonly configurationService: IConfigurationService,172@IKeybindingService protected readonly keybindingService: IKeybindingService,173) {174super(null, action, options);175176this.options = options;177178this._register(this.themeService.onDidColorThemeChange(this.onThemeChange, this));179this._register(action.onDidChangeCompositeBarActionItem(() => this.update()));180this._register(Event.filter(keybindingService.onDidUpdateKeybindings, () => this.keybindingLabel !== this.computeKeybindingLabel())(() => this.updateTitle()));181this._register(action.onDidChangeActivity(() => this.updateActivity()));182}183184protected get compositeBarActionItem(): ICompositeBarActionItem {185return (this._action as CompositeBarAction).compositeBarActionItem;186}187188protected updateStyles(): void {189const theme = this.themeService.getColorTheme();190const colors = this.options.colors(theme);191192if (this.label) {193if (this.options.icon) {194const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor;195if (this.compositeBarActionItem.iconUrl) {196// Apply background color to activity bar item provided with iconUrls197this.label.style.backgroundColor = foreground ? foreground.toString() : '';198this.label.style.color = '';199} else {200// Apply foreground color to activity bar items provided with codicons201this.label.style.color = foreground ? foreground.toString() : '';202this.label.style.backgroundColor = '';203}204} else {205const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor;206const borderBottomColor = this._action.checked ? colors.activeBorderBottomColor : null;207this.label.style.color = foreground ? foreground.toString() : '';208this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : '';209}210211this.container.style.setProperty('--insert-border-color', colors.dragAndDropBorder ? colors.dragAndDropBorder.toString() : '');212}213214// Badge215if (this.badgeContent) {216const badgeStyles = this.getActivities()[0]?.badge.getColors(theme);217const badgeFg = badgeStyles?.badgeForeground ?? colors.badgeForeground ?? theme.getColor(badgeForeground);218const badgeBg = badgeStyles?.badgeBackground ?? colors.badgeBackground ?? theme.getColor(badgeBackground);219const contrastBorderColor = badgeStyles?.badgeBorder ?? theme.getColor(contrastBorder);220221this.badgeContent.style.color = badgeFg ? badgeFg.toString() : '';222this.badgeContent.style.backgroundColor = badgeBg ? badgeBg.toString() : '';223224this.badgeContent.style.borderStyle = contrastBorderColor && !this.options.compact ? 'solid' : '';225this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : '';226this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : '';227}228}229230override render(container: HTMLElement): void {231super.render(container);232233this.container = container;234if (this.options.icon) {235this.container.classList.add('icon');236}237238if (this.options.hasPopup) {239this.container.setAttribute('role', 'button');240this.container.setAttribute('aria-haspopup', 'true');241} else {242this.container.setAttribute('role', 'tab');243}244245// Try hard to prevent keyboard only focus feedback when using mouse246this._register(addDisposableListener(this.container, EventType.MOUSE_DOWN, () => {247this.container.classList.add('clicked');248}));249250this._register(addDisposableListener(this.container, EventType.MOUSE_UP, () => {251if (this.mouseUpTimeout) {252clearTimeout(this.mouseUpTimeout);253}254255this.mouseUpTimeout = setTimeout(() => {256this.container.classList.remove('clicked');257}, 800); // delayed to prevent focus feedback from showing on mouse up258}));259260this._register(this.hoverService.setupDelayedHover(this.container, () => ({261content: this.computeTitle(),262position: {263hoverPosition: this.options.hoverOptions.position(),264},265persistence: {266hideOnKeyDown: true,267},268appearance: {269showPointer: true,270compact: true,271}272}), { groupId: 'composite-bar-actions' }));273274// Label275this.label = append(container, $('a'));276277// Badge278this.badge = append(container, $('.badge'));279this.badgeContent = append(this.badge, $('.badge-content'));280281// pane composite bar active border + background282append(container, $('.active-item-indicator'));283284hide(this.badge);285286this.update();287this.updateStyles();288this.updateTitle();289}290291private onThemeChange(theme: IColorTheme): void {292this.updateStyles();293}294295protected update(): void {296this.updateLabel();297this.updateActivity();298this.updateTitle();299this.updateStyles();300}301302private getActivities(): IActivity[] {303if (this._action instanceof CompositeBarAction) {304return this._action.activities;305}306return [];307}308309protected updateActivity(): void {310if (!this.badge || !this.badgeContent || !(this._action instanceof CompositeBarAction)) {311return;312}313314const { badges, type } = this.getVisibleBadges(this.getActivities());315316this.badgeDisposable.value = new DisposableStore();317318clearNode(this.badgeContent);319hide(this.badge);320321const shouldRenderBadges = this.badgesEnabled(this.compositeBarActionItem.id);322323if (badges.length > 0 && shouldRenderBadges) {324325const classes: string[] = [];326327if (this.options.compact) {328classes.push('compact');329}330331// Progress332if (type === 'progress') {333show(this.badge);334classes.push('progress-badge');335}336337// Number338else if (type === 'number') {339const total = badges.reduce((r, b) => r + (b instanceof NumberBadge ? b.number : 0), 0);340if (total > 0) {341let badgeNumber = total.toString();342if (total > 999) {343const noOfThousands = total / 1000;344const floor = Math.floor(noOfThousands);345badgeNumber = noOfThousands > floor ? `${floor}K+` : `${noOfThousands}K`;346}347if (this.options.compact && badgeNumber.length >= 3) {348classes.push('compact-content');349}350this.badgeContent.textContent = badgeNumber;351show(this.badge);352}353}354355// Icon356else if (type === 'icon') {357classes.push('icon-badge');358const badgeContentClassess = ['icon-overlay', ...ThemeIcon.asClassNameArray((badges[0] as IconBadge).icon)];359this.badgeContent.classList.add(...badgeContentClassess);360this.badgeDisposable.value.add(toDisposable(() => this.badgeContent?.classList.remove(...badgeContentClassess)));361show(this.badge);362}363364if (classes.length) {365this.badge.classList.add(...classes);366this.badgeDisposable.value.add(toDisposable(() => this.badge.classList.remove(...classes)));367}368369}370371this.updateTitle();372this.updateStyles();373}374375private getVisibleBadges(activities: IActivity[]): { badges: IBadge[]; type: 'progress' | 'icon' | 'number' | undefined } {376const progressBadges = activities.filter(activity => activity.badge instanceof ProgressBadge).map(activity => activity.badge);377if (progressBadges.length > 0) {378return { badges: progressBadges, type: 'progress' };379}380381const iconBadges = activities.filter(activity => activity.badge instanceof IconBadge).map(activity => activity.badge);382if (iconBadges.length > 0) {383return { badges: iconBadges, type: 'icon' };384}385386const numberBadges = activities.filter(activity => activity.badge instanceof NumberBadge).map(activity => activity.badge);387if (numberBadges.length > 0) {388return { badges: numberBadges, type: 'number' };389}390391return { badges: [], type: undefined };392}393394protected override updateLabel(): void {395this.label.className = 'action-label';396397if (this.compositeBarActionItem.classNames) {398this.label.classList.add(...this.compositeBarActionItem.classNames);399}400401if (!this.options.icon) {402this.label.textContent = this.action.label;403}404}405406private updateTitle(): void {407const title = this.computeTitle();408[this.label, this.badge, this.container].forEach(element => {409if (element) {410element.setAttribute('aria-label', title);411element.setAttribute('title', '');412element.removeAttribute('title');413}414});415}416417protected computeTitle(): string {418this.keybindingLabel = this.computeKeybindingLabel();419let title = this.keybindingLabel ? localize('titleKeybinding', "{0} ({1})", this.compositeBarActionItem.name, this.keybindingLabel) : this.compositeBarActionItem.name;420421const badges = this.getVisibleBadges((this.action as CompositeBarAction).activities).badges;422for (const badge of badges) {423const description = badge.getDescription();424if (!description) {425continue;426}427title = `${title} - ${badge.getDescription()}`;428}429430return title;431}432433private computeKeybindingLabel(): string | undefined | null {434const keybinding = this.compositeBarActionItem.keybindingId ? this.keybindingService.lookupKeybinding(this.compositeBarActionItem.keybindingId) : null;435436return keybinding?.getLabel();437}438439override dispose(): void {440super.dispose();441442if (this.mouseUpTimeout) {443clearTimeout(this.mouseUpTimeout);444}445446this.badge.remove();447}448}449450export class CompositeOverflowActivityAction extends CompositeBarAction {451452constructor(453private showMenu: () => void454) {455super({456id: 'additionalComposites.action',457name: localize('additionalViews', "Additional Views"),458classNames: ThemeIcon.asClassNameArray(Codicon.more)459});460}461462override async run(): Promise<void> {463this.showMenu();464}465}466467export class CompositeOverflowActivityActionViewItem extends CompositeBarActionViewItem {468469constructor(470action: CompositeBarAction,471private getOverflowingComposites: () => { id: string; name?: string }[],472private getActiveCompositeId: () => string | undefined,473private getBadge: (compositeId: string) => IBadge,474private getCompositeOpenAction: (compositeId: string) => IAction,475colors: (theme: IColorTheme) => ICompositeBarColors,476hoverOptions: IActivityHoverOptions,477@IContextMenuService private readonly contextMenuService: IContextMenuService,478@IThemeService themeService: IThemeService,479@IHoverService hoverService: IHoverService,480@IConfigurationService configurationService: IConfigurationService,481@IKeybindingService keybindingService: IKeybindingService,482) {483super(action, { icon: true, colors, hasPopup: true, hoverOptions }, () => true, themeService, hoverService, configurationService, keybindingService);484}485486showMenu(): void {487this.contextMenuService.showContextMenu({488getAnchor: () => this.container,489getActions: () => this.getActions(),490getCheckedActionsRepresentation: () => 'radio',491});492}493494private getActions(): IAction[] {495return this.getOverflowingComposites().map(composite => {496const action = this.getCompositeOpenAction(composite.id);497action.checked = this.getActiveCompositeId() === action.id;498499const badge = this.getBadge(composite.id);500let suffix: string | number | undefined;501if (badge instanceof NumberBadge) {502suffix = badge.number;503}504505if (suffix) {506action.label = localize('numberBadge', "{0} ({1})", composite.name, suffix);507} else {508action.label = composite.name || '';509}510511return action;512});513}514}515516export class CompositeActionViewItem extends CompositeBarActionViewItem {517518constructor(519options: ICompositeBarActionViewItemOptions,520compositeActivityAction: CompositeBarAction,521private readonly toggleCompositePinnedAction: IAction,522private readonly toggleCompositeBadgeAction: IAction,523private readonly compositeContextMenuActionsProvider: (compositeId: string) => IAction[],524private readonly contextMenuActionsProvider: () => IAction[],525private readonly dndHandler: ICompositeDragAndDrop,526private readonly compositeBar: ICompositeBar,527@IContextMenuService private readonly contextMenuService: IContextMenuService,528@IKeybindingService keybindingService: IKeybindingService,529@IInstantiationService instantiationService: IInstantiationService,530@IThemeService themeService: IThemeService,531@IHoverService hoverService: IHoverService,532@IConfigurationService configurationService: IConfigurationService,533@ICommandService private readonly commandService: ICommandService534) {535super(536compositeActivityAction,537options,538compositeBar.areBadgesEnabled.bind(compositeBar),539themeService,540hoverService,541configurationService,542keybindingService543);544}545546override render(container: HTMLElement): void {547super.render(container);548549this.updateChecked();550this.updateEnabled();551552this._register(addDisposableListener(this.container, EventType.CONTEXT_MENU, e => {553EventHelper.stop(e, true);554555this.showContextMenu(container);556}));557558// Allow to drag559let insertDropBefore: Before2D | undefined = undefined;560this._register(CompositeDragAndDropObserver.INSTANCE.registerDraggable(this.container, () => { return { type: 'composite', id: this.compositeBarActionItem.id }; }, {561onDragOver: e => {562const isValidMove = e.dragAndDropData.getData().id !== this.compositeBarActionItem.id && this.dndHandler.onDragOver(e.dragAndDropData, this.compositeBarActionItem.id, e.eventData);563toggleDropEffect(e.eventData.dataTransfer, 'move', isValidMove);564insertDropBefore = this.updateFromDragging(container, isValidMove, e.eventData);565},566onDragLeave: e => {567insertDropBefore = this.updateFromDragging(container, false, e.eventData);568},569onDragEnd: e => {570insertDropBefore = this.updateFromDragging(container, false, e.eventData);571},572onDrop: e => {573EventHelper.stop(e.eventData, true);574this.dndHandler.drop(e.dragAndDropData, this.compositeBarActionItem.id, e.eventData, insertDropBefore);575insertDropBefore = this.updateFromDragging(container, false, e.eventData);576},577onDragStart: e => {578if (e.dragAndDropData.getData().id !== this.compositeBarActionItem.id) {579return;580}581582if (e.eventData.dataTransfer) {583e.eventData.dataTransfer.effectAllowed = 'move';584}585586this.blur(); // Remove focus indicator when dragging587}588}));589590// Activate on drag over to reveal targets591[this.badge, this.label].forEach(element => this._register(new DelayedDragHandler(element, () => {592if (!this.action.checked) {593this.action.run();594}595})));596597this.updateStyles();598}599600private updateFromDragging(element: HTMLElement, showFeedback: boolean, event: DragEvent): Before2D | undefined {601const rect = element.getBoundingClientRect();602const posX = event.clientX;603const posY = event.clientY;604const height = rect.bottom - rect.top;605const width = rect.right - rect.left;606607const forceTop = posY <= rect.top + height * 0.4;608const forceBottom = posY > rect.bottom - height * 0.4;609const preferTop = posY <= rect.top + height * 0.5;610611const forceLeft = posX <= rect.left + width * 0.4;612const forceRight = posX > rect.right - width * 0.4;613const preferLeft = posX <= rect.left + width * 0.5;614615const classes = element.classList;616const lastClasses = {617vertical: classes.contains('top') ? 'top' : (classes.contains('bottom') ? 'bottom' : undefined),618horizontal: classes.contains('left') ? 'left' : (classes.contains('right') ? 'right' : undefined)619};620621const top = forceTop || (preferTop && !lastClasses.vertical) || (!forceBottom && lastClasses.vertical === 'top');622const bottom = forceBottom || (!preferTop && !lastClasses.vertical) || (!forceTop && lastClasses.vertical === 'bottom');623const left = forceLeft || (preferLeft && !lastClasses.horizontal) || (!forceRight && lastClasses.horizontal === 'left');624const right = forceRight || (!preferLeft && !lastClasses.horizontal) || (!forceLeft && lastClasses.horizontal === 'right');625626element.classList.toggle('top', showFeedback && top);627element.classList.toggle('bottom', showFeedback && bottom);628element.classList.toggle('left', showFeedback && left);629element.classList.toggle('right', showFeedback && right);630631if (!showFeedback) {632return undefined;633}634635return { verticallyBefore: top, horizontallyBefore: left };636}637638private showContextMenu(container: HTMLElement): void {639const actions: IAction[] = [];640641if (this.compositeBarActionItem.keybindingId) {642actions.push(createConfigureKeybindingAction(this.commandService, this.keybindingService, this.compositeBarActionItem.keybindingId));643}644645actions.push(this.toggleCompositePinnedAction, this.toggleCompositeBadgeAction);646647const compositeContextMenuActions = this.compositeContextMenuActionsProvider(this.compositeBarActionItem.id);648if (compositeContextMenuActions.length) {649actions.push(...compositeContextMenuActions);650}651652const isPinned = this.compositeBar.isPinned(this.compositeBarActionItem.id);653if (isPinned) {654this.toggleCompositePinnedAction.label = localize('hide', "Hide '{0}'", this.compositeBarActionItem.name);655this.toggleCompositePinnedAction.checked = false;656this.toggleCompositePinnedAction.enabled = this.compositeBar.getPinnedCompositeIds().length > 1;657} else {658this.toggleCompositePinnedAction.label = localize('keep', "Keep '{0}'", this.compositeBarActionItem.name);659this.toggleCompositePinnedAction.enabled = true;660}661662const isBadgeEnabled = this.compositeBar.areBadgesEnabled(this.compositeBarActionItem.id);663if (isBadgeEnabled) {664this.toggleCompositeBadgeAction.label = localize('hideBadge', "Hide Badge");665} else {666this.toggleCompositeBadgeAction.label = localize('showBadge', "Show Badge");667}668669const otherActions = this.contextMenuActionsProvider();670if (otherActions.length) {671actions.push(new Separator());672actions.push(...otherActions);673}674675const elementPosition = getDomNodePagePosition(container);676const anchor = {677x: Math.floor(elementPosition.left + (elementPosition.width / 2)),678y: elementPosition.top + elementPosition.height679};680681this.contextMenuService.showContextMenu({682getAnchor: () => anchor,683getActions: () => actions,684getActionsContext: () => this.compositeBarActionItem.id685});686}687688protected override updateChecked(): void {689if (this.action.checked) {690this.container.classList.add('checked');691this.container.setAttribute('aria-label', this.getTooltip() ?? this.container.title);692this.container.setAttribute('aria-expanded', 'true');693this.container.setAttribute('aria-selected', 'true');694} else {695this.container.classList.remove('checked');696this.container.setAttribute('aria-label', this.getTooltip() ?? this.container.title);697this.container.setAttribute('aria-expanded', 'false');698this.container.setAttribute('aria-selected', 'false');699}700701this.updateStyles();702}703704protected override updateEnabled(): void {705if (!this.element) {706return;707}708709if (this.action.enabled) {710this.element.classList.remove('disabled');711} else {712this.element.classList.add('disabled');713}714}715716override dispose(): void {717super.dispose();718719this.label.remove();720}721}722723export class ToggleCompositePinnedAction extends Action {724725constructor(726private activity: ICompositeBarActionItem | undefined,727private compositeBar: ICompositeBar728) {729super('show.toggleCompositePinned', activity ? activity.name : localize('toggle', "Toggle View Pinned"));730731this.checked = !!this.activity && this.compositeBar.isPinned(this.activity.id);732}733734override async run(context: string): Promise<void> {735const id = this.activity ? this.activity.id : context;736737if (this.compositeBar.isPinned(id)) {738this.compositeBar.unpin(id);739} else {740this.compositeBar.pin(id);741}742}743}744745export class ToggleCompositeBadgeAction extends Action {746constructor(747private compositeBarActionItem: ICompositeBarActionItem | undefined,748private compositeBar: ICompositeBar749) {750super('show.toggleCompositeBadge', compositeBarActionItem ? compositeBarActionItem.name : localize('toggleBadge', "Toggle View Badge"));751752this.checked = false;753}754755override async run(context: string): Promise<void> {756const id = this.compositeBarActionItem ? this.compositeBarActionItem.id : context;757this.compositeBar.toggleBadgeEnablement(id);758}759}760761export class SwitchCompositeViewAction extends Action2 {762constructor(763desc: Readonly<IAction2Options>,764private readonly location: ViewContainerLocation,765private readonly offset: number766) {767super(desc);768}769770async run(accessor: ServicesAccessor): Promise<void> {771const paneCompositeService = accessor.get(IPaneCompositePartService);772773const activeComposite = paneCompositeService.getActivePaneComposite(this.location);774if (!activeComposite) {775return;776}777778let targetCompositeId: string | undefined;779780const visibleCompositeIds = paneCompositeService.getVisiblePaneCompositeIds(this.location);781for (let i = 0; i < visibleCompositeIds.length; i++) {782if (visibleCompositeIds[i] === activeComposite.getId()) {783targetCompositeId = visibleCompositeIds[(i + visibleCompositeIds.length + this.offset) % visibleCompositeIds.length];784break;785}786}787788if (typeof targetCompositeId !== 'undefined') {789await paneCompositeService.openPaneComposite(targetCompositeId, this.location, true);790}791}792}793794795