Path: blob/main/src/vs/workbench/browser/parts/compositeBarActions.ts
5281 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';31import { HoverStyle } from '../../../base/browser/ui/hover/hover.js';3233export interface ICompositeBar {3435/**36* Unpins a composite from the composite bar.37*/38unpin(compositeId: string): void;3940/**41* Pin a composite inside the composite bar.42*/43pin(compositeId: string): void;4445/**46* Find out if a composite is pinned in the composite bar.47*/48isPinned(compositeId: string): boolean;4950/**51* Get pinned composite ids in the composite bar.52*/53getPinnedCompositeIds(): string[];5455/**56* Returns if badges are enabled for that specified composite.57* @param compositeId The id of the composite to check58*/59areBadgesEnabled(compositeId: string): boolean;6061/**62* Toggles whether or not badges are shown on that particular composite.63* @param compositeId The composite to toggle badge enablement for64*/65toggleBadgeEnablement(compositeId: string): void;6667/**68* Reorder composite ordering by moving a composite to the location of another composite.69*/70move(compositeId: string, tocompositeId: string): void;71}7273export interface ICompositeBarActionItem {74id: string;75name: string;76keybindingId?: string;77classNames?: string[];78iconUrl?: URI;79}8081export class CompositeBarAction extends Action {8283private readonly _onDidChangeCompositeBarActionItem = this._register(new Emitter<CompositeBarAction>());84readonly onDidChangeCompositeBarActionItem = this._onDidChangeCompositeBarActionItem.event;8586private readonly _onDidChangeActivity = this._register(new Emitter<IActivity[]>());87readonly onDidChangeActivity = this._onDidChangeActivity.event;8889private _activities: IActivity[] = [];9091constructor(private item: ICompositeBarActionItem) {92super(item.id, item.name, item.classNames?.join(' '), true);93}9495get compositeBarActionItem(): ICompositeBarActionItem {96return this.item;97}9899set compositeBarActionItem(item: ICompositeBarActionItem) {100this._label = item.name;101this.item = item;102this._onDidChangeCompositeBarActionItem.fire(this);103}104105get activities(): IActivity[] {106return this._activities;107}108109set activities(activities: IActivity[]) {110this._activities = activities;111this._onDidChangeActivity.fire(activities);112}113114activate(): void {115if (!this.checked) {116this._setChecked(true);117}118}119120deactivate(): void {121if (this.checked) {122this._setChecked(false);123}124}125126}127128export interface ICompositeBarColors {129readonly activeBackgroundColor?: Color;130readonly inactiveBackgroundColor?: Color;131readonly activeBorderColor?: Color;132readonly activeBackground?: Color;133readonly activeBorderBottomColor?: Color;134readonly activeForegroundColor?: Color;135readonly inactiveForegroundColor?: Color;136readonly badgeBackground?: Color;137readonly badgeForeground?: Color;138readonly dragAndDropBorder?: Color;139}140141export interface IActivityHoverOptions {142readonly position: () => HoverPosition;143}144145export interface ICompositeBarActionViewItemOptions extends IActionViewItemOptions {146readonly icon?: boolean;147readonly colors: (theme: IColorTheme) => ICompositeBarColors;148149readonly hoverOptions: IActivityHoverOptions;150readonly hasPopup?: boolean;151readonly compact?: boolean;152}153154export class CompositeBarActionViewItem extends BaseActionViewItem {155156protected container!: HTMLElement;157protected label!: HTMLElement;158protected badge!: HTMLElement;159protected override readonly options: ICompositeBarActionViewItemOptions;160161private badgeContent: HTMLElement | undefined;162private readonly badgeDisposable = this._register(new MutableDisposable<DisposableStore>());163private mouseUpTimeout: Timeout | undefined;164private keybindingLabel: string | undefined | null;165166constructor(167action: CompositeBarAction,168options: ICompositeBarActionViewItemOptions,169private readonly badgesEnabled: (compositeId: string) => boolean,170@IThemeService protected readonly themeService: IThemeService,171@IHoverService private readonly hoverService: IHoverService,172@IConfigurationService protected readonly configurationService: IConfigurationService,173@IKeybindingService protected readonly keybindingService: IKeybindingService,174) {175super(null, action, options);176177this.options = options;178179this._register(this.themeService.onDidColorThemeChange(this.onThemeChange, this));180this._register(action.onDidChangeCompositeBarActionItem(() => this.update()));181this._register(Event.filter(keybindingService.onDidUpdateKeybindings, () => this.keybindingLabel !== this.computeKeybindingLabel())(() => this.updateTitle()));182this._register(action.onDidChangeActivity(() => this.updateActivity()));183}184185protected get compositeBarActionItem(): ICompositeBarActionItem {186return (this._action as CompositeBarAction).compositeBarActionItem;187}188189protected updateStyles(): void {190const theme = this.themeService.getColorTheme();191const colors = this.options.colors(theme);192193if (this.label) {194if (this.options.icon) {195const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor;196if (this.compositeBarActionItem.iconUrl) {197// Apply background color to activity bar item provided with iconUrls198this.label.style.backgroundColor = foreground ? foreground.toString() : '';199this.label.style.color = '';200} else {201// Apply foreground color to activity bar items provided with codicons202this.label.style.color = foreground ? foreground.toString() : '';203this.label.style.backgroundColor = '';204}205} else {206const foreground = this._action.checked ? colors.activeForegroundColor : colors.inactiveForegroundColor;207const borderBottomColor = this._action.checked ? colors.activeBorderBottomColor : null;208this.label.style.color = foreground ? foreground.toString() : '';209this.label.style.borderBottomColor = borderBottomColor ? borderBottomColor.toString() : '';210}211212this.container.style.setProperty('--insert-border-color', colors.dragAndDropBorder ? colors.dragAndDropBorder.toString() : '');213}214215// Badge216if (this.badgeContent) {217const badgeStyles = this.getActivities()[0]?.badge.getColors(theme);218const badgeFg = badgeStyles?.badgeForeground ?? colors.badgeForeground ?? theme.getColor(badgeForeground);219const badgeBg = badgeStyles?.badgeBackground ?? colors.badgeBackground ?? theme.getColor(badgeBackground);220const contrastBorderColor = badgeStyles?.badgeBorder ?? theme.getColor(contrastBorder);221222this.badgeContent.style.color = badgeFg ? badgeFg.toString() : '';223this.badgeContent.style.backgroundColor = badgeBg ? badgeBg.toString() : '';224225this.badgeContent.style.borderStyle = contrastBorderColor && !this.options.compact ? 'solid' : '';226this.badgeContent.style.borderWidth = contrastBorderColor ? '1px' : '';227this.badgeContent.style.borderColor = contrastBorderColor ? contrastBorderColor.toString() : '';228}229}230231override render(container: HTMLElement): void {232super.render(container);233234this.container = container;235if (this.options.icon) {236this.container.classList.add('icon');237}238239// Use 'tab' inside tablist, 'button' for popup items outside tablist240const role = this.options.isTabList || !this.options.hasPopup ? 'tab' : 'button';241this.container.setAttribute('role', role);242if (this.options.hasPopup) {243this.container.setAttribute('aria-haspopup', 'true');244}245246// Try hard to prevent keyboard only focus feedback when using mouse247this._register(addDisposableListener(this.container, EventType.MOUSE_DOWN, () => {248this.container.classList.add('clicked');249}));250251this._register(addDisposableListener(this.container, EventType.MOUSE_UP, () => {252if (this.mouseUpTimeout) {253clearTimeout(this.mouseUpTimeout);254}255256this.mouseUpTimeout = setTimeout(() => {257this.container.classList.remove('clicked');258}, 800); // delayed to prevent focus feedback from showing on mouse up259}));260261this._register(this.hoverService.setupDelayedHover(this.container, () => ({262content: this.computeTitle(),263style: HoverStyle.Pointer,264position: {265hoverPosition: this.options.hoverOptions.position(),266},267persistence: {268hideOnKeyDown: true,269},270}), { groupId: 'composite-bar-actions' }));271272// Label273this.label = append(container, $('a'));274275// Badge276this.badge = append(container, $('.badge'));277this.badgeContent = append(this.badge, $('.badge-content'));278279// pane composite bar active border + background280append(container, $('.active-item-indicator'));281282hide(this.badge);283284this.update();285this.updateStyles();286this.updateTitle();287}288289private onThemeChange(theme: IColorTheme): void {290this.updateStyles();291}292293protected update(): void {294this.updateLabel();295this.updateActivity();296this.updateTitle();297this.updateStyles();298}299300private getActivities(): IActivity[] {301if (this._action instanceof CompositeBarAction) {302return this._action.activities;303}304return [];305}306307protected updateActivity(): void {308if (!this.badge || !this.badgeContent || !(this._action instanceof CompositeBarAction)) {309return;310}311312const { badges, type } = this.getVisibleBadges(this.getActivities());313314this.badgeDisposable.value = new DisposableStore();315316clearNode(this.badgeContent);317hide(this.badge);318319const shouldRenderBadges = this.badgesEnabled(this.compositeBarActionItem.id);320321if (badges.length > 0 && shouldRenderBadges) {322323const classes: string[] = [];324325if (this.options.compact) {326classes.push('compact');327}328329// Progress330if (type === 'progress') {331show(this.badge);332classes.push('progress-badge');333}334335// Number336else if (type === 'number') {337const total = badges.reduce((r, b) => r + (b instanceof NumberBadge ? b.number : 0), 0);338if (total > 0) {339let badgeNumber = total.toString();340if (total > 999) {341const noOfThousands = total / 1000;342const floor = Math.floor(noOfThousands);343badgeNumber = noOfThousands > floor ? `${floor}K+` : `${noOfThousands}K`;344}345if (this.options.compact && badgeNumber.length >= 3) {346classes.push('compact-content');347}348this.badgeContent.textContent = badgeNumber;349show(this.badge);350}351}352353// Icon354else if (type === 'icon') {355classes.push('icon-badge');356const badgeContentClassess = ['icon-overlay', ...ThemeIcon.asClassNameArray((badges[0] as IconBadge).icon)];357this.badgeContent.classList.add(...badgeContentClassess);358this.badgeDisposable.value.add(toDisposable(() => this.badgeContent?.classList.remove(...badgeContentClassess)));359show(this.badge);360}361362if (classes.length) {363this.badge.classList.add(...classes);364this.badgeDisposable.value.add(toDisposable(() => this.badge.classList.remove(...classes)));365}366367}368369this.updateTitle();370this.updateStyles();371}372373private getVisibleBadges(activities: IActivity[]): { badges: IBadge[]; type: 'progress' | 'icon' | 'number' | undefined } {374const progressBadges = activities.filter(activity => activity.badge instanceof ProgressBadge).map(activity => activity.badge);375if (progressBadges.length > 0) {376return { badges: progressBadges, type: 'progress' };377}378379const iconBadges = activities.filter(activity => activity.badge instanceof IconBadge).map(activity => activity.badge);380if (iconBadges.length > 0) {381return { badges: iconBadges, type: 'icon' };382}383384const numberBadges = activities.filter(activity => activity.badge instanceof NumberBadge).map(activity => activity.badge);385if (numberBadges.length > 0) {386return { badges: numberBadges, type: 'number' };387}388389return { badges: [], type: undefined };390}391392protected override updateLabel(): void {393this.label.className = 'action-label';394395if (this.compositeBarActionItem.classNames) {396this.label.classList.add(...this.compositeBarActionItem.classNames);397}398399if (!this.options.icon) {400this.label.textContent = this.action.label;401}402}403404private updateTitle(): void {405const title = this.computeTitle();406[this.label, this.badge, this.container].forEach(element => {407if (element) {408element.setAttribute('aria-label', title);409element.setAttribute('title', '');410element.removeAttribute('title');411}412});413}414415protected computeTitle(): string {416this.keybindingLabel = this.computeKeybindingLabel();417let title = this.keybindingLabel ? localize('titleKeybinding', "{0} ({1})", this.compositeBarActionItem.name, this.keybindingLabel) : this.compositeBarActionItem.name;418419const badges = this.getVisibleBadges((this.action as CompositeBarAction).activities).badges;420for (const badge of badges) {421const description = badge.getDescription();422if (!description) {423continue;424}425title = `${title} - ${badge.getDescription()}`;426}427428return title;429}430431private computeKeybindingLabel(): string | undefined | null {432const keybinding = this.compositeBarActionItem.keybindingId ? this.keybindingService.lookupKeybinding(this.compositeBarActionItem.keybindingId) : null;433434return keybinding?.getLabel();435}436437override dispose(): void {438super.dispose();439440if (this.mouseUpTimeout) {441clearTimeout(this.mouseUpTimeout);442}443444this.badge.remove();445}446}447448export class CompositeOverflowActivityAction extends CompositeBarAction {449450constructor(451private showMenu: () => void452) {453super({454id: 'additionalComposites.action',455name: localize('additionalViews', "Additional Views"),456classNames: ThemeIcon.asClassNameArray(Codicon.more)457});458}459460override async run(): Promise<void> {461this.showMenu();462}463}464465export class CompositeOverflowActivityActionViewItem extends CompositeBarActionViewItem {466467constructor(468action: CompositeBarAction,469private getOverflowingComposites: () => { id: string; name?: string }[],470private getActiveCompositeId: () => string | undefined,471private getBadge: (compositeId: string) => IBadge,472private getCompositeOpenAction: (compositeId: string) => IAction,473colors: (theme: IColorTheme) => ICompositeBarColors,474hoverOptions: IActivityHoverOptions,475@IContextMenuService private readonly contextMenuService: IContextMenuService,476@IThemeService themeService: IThemeService,477@IHoverService hoverService: IHoverService,478@IConfigurationService configurationService: IConfigurationService,479@IKeybindingService keybindingService: IKeybindingService,480) {481super(action, { icon: true, colors, hasPopup: true, hoverOptions, isTabList: true }, () => true, themeService, hoverService, configurationService, keybindingService);482}483484showMenu(): void {485this.contextMenuService.showContextMenu({486getAnchor: () => this.container,487getActions: () => this.getActions(),488getCheckedActionsRepresentation: () => 'radio',489});490}491492private getActions(): IAction[] {493return this.getOverflowingComposites().map(composite => {494const action = this.getCompositeOpenAction(composite.id);495action.checked = this.getActiveCompositeId() === action.id;496497const badge = this.getBadge(composite.id);498let suffix: string | number | undefined;499if (badge instanceof NumberBadge) {500suffix = badge.number;501}502503if (suffix) {504action.label = localize('numberBadge', "{0} ({1})", composite.name, suffix);505} else {506action.label = composite.name || '';507}508509return action;510});511}512}513514export class CompositeActionViewItem extends CompositeBarActionViewItem {515516constructor(517options: ICompositeBarActionViewItemOptions,518compositeActivityAction: CompositeBarAction,519private readonly toggleCompositePinnedAction: IAction,520private readonly toggleCompositeBadgeAction: IAction,521private readonly compositeContextMenuActionsProvider: (compositeId: string) => IAction[],522private readonly contextMenuActionsProvider: () => IAction[],523private readonly dndHandler: ICompositeDragAndDrop,524private readonly compositeBar: ICompositeBar,525@IContextMenuService private readonly contextMenuService: IContextMenuService,526@IKeybindingService keybindingService: IKeybindingService,527@IInstantiationService instantiationService: IInstantiationService,528@IThemeService themeService: IThemeService,529@IHoverService hoverService: IHoverService,530@IConfigurationService configurationService: IConfigurationService,531@ICommandService private readonly commandService: ICommandService532) {533super(534compositeActivityAction,535options,536compositeBar.areBadgesEnabled.bind(compositeBar),537themeService,538hoverService,539configurationService,540keybindingService541);542}543544override render(container: HTMLElement): void {545super.render(container);546547this.updateChecked();548this.updateEnabled();549550this._register(addDisposableListener(this.container, EventType.CONTEXT_MENU, e => {551EventHelper.stop(e, true);552553this.showContextMenu(container);554}));555556// Allow to drag557let insertDropBefore: Before2D | undefined = undefined;558this._register(CompositeDragAndDropObserver.INSTANCE.registerDraggable(this.container, () => { return { type: 'composite', id: this.compositeBarActionItem.id }; }, {559onDragOver: e => {560const isValidMove = e.dragAndDropData.getData().id !== this.compositeBarActionItem.id && this.dndHandler.onDragOver(e.dragAndDropData, this.compositeBarActionItem.id, e.eventData);561toggleDropEffect(e.eventData.dataTransfer, 'move', isValidMove);562insertDropBefore = this.updateFromDragging(container, isValidMove, e.eventData);563},564onDragLeave: e => {565insertDropBefore = this.updateFromDragging(container, false, e.eventData);566},567onDragEnd: e => {568insertDropBefore = this.updateFromDragging(container, false, e.eventData);569},570onDrop: e => {571EventHelper.stop(e.eventData, true);572this.dndHandler.drop(e.dragAndDropData, this.compositeBarActionItem.id, e.eventData, insertDropBefore);573insertDropBefore = this.updateFromDragging(container, false, e.eventData);574},575onDragStart: e => {576if (e.dragAndDropData.getData().id !== this.compositeBarActionItem.id) {577return;578}579580if (e.eventData.dataTransfer) {581e.eventData.dataTransfer.effectAllowed = 'move';582}583584this.blur(); // Remove focus indicator when dragging585}586}));587588// Activate on drag over to reveal targets589[this.badge, this.label].forEach(element => this._register(new DelayedDragHandler(element, () => {590if (!this.action.checked) {591this.action.run();592}593})));594595this.updateStyles();596}597598private updateFromDragging(element: HTMLElement, showFeedback: boolean, event: DragEvent): Before2D | undefined {599const rect = element.getBoundingClientRect();600const posX = event.clientX;601const posY = event.clientY;602const height = rect.bottom - rect.top;603const width = rect.right - rect.left;604605const forceTop = posY <= rect.top + height * 0.4;606const forceBottom = posY > rect.bottom - height * 0.4;607const preferTop = posY <= rect.top + height * 0.5;608609const forceLeft = posX <= rect.left + width * 0.4;610const forceRight = posX > rect.right - width * 0.4;611const preferLeft = posX <= rect.left + width * 0.5;612613const classes = element.classList;614const lastClasses = {615vertical: classes.contains('top') ? 'top' : (classes.contains('bottom') ? 'bottom' : undefined),616horizontal: classes.contains('left') ? 'left' : (classes.contains('right') ? 'right' : undefined)617};618619const top = forceTop || (preferTop && !lastClasses.vertical) || (!forceBottom && lastClasses.vertical === 'top');620const bottom = forceBottom || (!preferTop && !lastClasses.vertical) || (!forceTop && lastClasses.vertical === 'bottom');621const left = forceLeft || (preferLeft && !lastClasses.horizontal) || (!forceRight && lastClasses.horizontal === 'left');622const right = forceRight || (!preferLeft && !lastClasses.horizontal) || (!forceLeft && lastClasses.horizontal === 'right');623624element.classList.toggle('top', showFeedback && top);625element.classList.toggle('bottom', showFeedback && bottom);626element.classList.toggle('left', showFeedback && left);627element.classList.toggle('right', showFeedback && right);628629if (!showFeedback) {630return undefined;631}632633return { verticallyBefore: top, horizontallyBefore: left };634}635636private showContextMenu(container: HTMLElement): void {637const actions: IAction[] = [];638639if (this.compositeBarActionItem.keybindingId) {640actions.push(createConfigureKeybindingAction(this.commandService, this.keybindingService, this.compositeBarActionItem.keybindingId));641}642643actions.push(this.toggleCompositePinnedAction, this.toggleCompositeBadgeAction);644645const compositeContextMenuActions = this.compositeContextMenuActionsProvider(this.compositeBarActionItem.id);646if (compositeContextMenuActions.length) {647actions.push(...compositeContextMenuActions);648}649650const isPinned = this.compositeBar.isPinned(this.compositeBarActionItem.id);651if (isPinned) {652this.toggleCompositePinnedAction.label = localize('hide', "Hide '{0}'", this.compositeBarActionItem.name);653this.toggleCompositePinnedAction.checked = false;654this.toggleCompositePinnedAction.enabled = this.compositeBar.getPinnedCompositeIds().length > 1;655} else {656this.toggleCompositePinnedAction.label = localize('keep', "Keep '{0}'", this.compositeBarActionItem.name);657this.toggleCompositePinnedAction.enabled = true;658}659660const isBadgeEnabled = this.compositeBar.areBadgesEnabled(this.compositeBarActionItem.id);661if (isBadgeEnabled) {662this.toggleCompositeBadgeAction.label = localize('hideBadge', "Hide Badge");663} else {664this.toggleCompositeBadgeAction.label = localize('showBadge', "Show Badge");665}666667const otherActions = this.contextMenuActionsProvider();668if (otherActions.length) {669actions.push(new Separator());670actions.push(...otherActions);671}672673const elementPosition = getDomNodePagePosition(container);674const anchor = {675x: Math.floor(elementPosition.left + (elementPosition.width / 2)),676y: elementPosition.top + elementPosition.height677};678679this.contextMenuService.showContextMenu({680getAnchor: () => anchor,681getActions: () => actions,682getActionsContext: () => this.compositeBarActionItem.id683});684}685686protected override updateChecked(): void {687if (this.action.checked) {688this.container.classList.add('checked');689this.container.setAttribute('aria-label', this.getTooltip() ?? this.container.title);690this.container.setAttribute('aria-expanded', 'true');691this.container.setAttribute('aria-selected', 'true');692} else {693this.container.classList.remove('checked');694this.container.setAttribute('aria-label', this.getTooltip() ?? this.container.title);695this.container.setAttribute('aria-expanded', 'false');696this.container.setAttribute('aria-selected', 'false');697}698699this.updateStyles();700}701702protected override updateEnabled(): void {703if (!this.element) {704return;705}706707if (this.action.enabled) {708this.element.classList.remove('disabled');709} else {710this.element.classList.add('disabled');711}712}713714override dispose(): void {715super.dispose();716717this.label.remove();718}719}720721export class ToggleCompositePinnedAction extends Action {722723constructor(724private activity: ICompositeBarActionItem | undefined,725private compositeBar: ICompositeBar726) {727super('show.toggleCompositePinned', activity ? activity.name : localize('toggle', "Toggle View Pinned"));728729this.checked = !!this.activity && this.compositeBar.isPinned(this.activity.id);730}731732override async run(context: string): Promise<void> {733const id = this.activity ? this.activity.id : context;734735if (this.compositeBar.isPinned(id)) {736this.compositeBar.unpin(id);737} else {738this.compositeBar.pin(id);739}740}741}742743export class ToggleCompositeBadgeAction extends Action {744constructor(745private compositeBarActionItem: ICompositeBarActionItem | undefined,746private compositeBar: ICompositeBar747) {748super('show.toggleCompositeBadge', compositeBarActionItem ? compositeBarActionItem.name : localize('toggleBadge', "Toggle View Badge"));749750this.checked = false;751}752753override async run(context: string): Promise<void> {754const id = this.compositeBarActionItem ? this.compositeBarActionItem.id : context;755this.compositeBar.toggleBadgeEnablement(id);756}757}758759export class SwitchCompositeViewAction extends Action2 {760constructor(761desc: Readonly<IAction2Options>,762private readonly location: ViewContainerLocation,763private readonly offset: number764) {765super(desc);766}767768async run(accessor: ServicesAccessor): Promise<void> {769const paneCompositeService = accessor.get(IPaneCompositePartService);770771const activeComposite = paneCompositeService.getActivePaneComposite(this.location);772if (!activeComposite) {773return;774}775776let targetCompositeId: string | undefined;777778const visibleCompositeIds = paneCompositeService.getVisiblePaneCompositeIds(this.location);779for (let i = 0; i < visibleCompositeIds.length; i++) {780if (visibleCompositeIds[i] === activeComposite.getId()) {781targetCompositeId = visibleCompositeIds[(i + visibleCompositeIds.length + this.offset) % visibleCompositeIds.length];782break;783}784}785786if (typeof targetCompositeId !== 'undefined') {787await paneCompositeService.openPaneComposite(targetCompositeId, this.location, true);788}789}790}791792793