diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java index ca6ca696f7ee..5605a0cc81d4 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceTreeView.java @@ -3,9 +3,10 @@ package com.intellij.execution.services; import com.intellij.execution.ExecutionBundle; import com.intellij.execution.services.ServiceModel.ServiceViewItem; +import com.intellij.execution.services.ServiceViewNavBarService.ServiceViewNavBarSelector; import com.intellij.ide.DataManager; import com.intellij.ide.dnd.DnDManager; -import com.intellij.ide.navigationToolbar.NavBarModel; +import com.intellij.ide.navbar.vm.NavBarVm; import com.intellij.ide.util.treeView.TreeState; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.*; @@ -32,6 +33,7 @@ import com.intellij.util.containers.JBTreeTraverser; import com.intellij.util.ui.StatusText; import com.intellij.util.ui.tree.TreeUtil; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.concurrency.AsyncPromise; import org.jetbrains.concurrency.Promise; import org.jetbrains.concurrency.Promises; @@ -70,9 +72,6 @@ final class ServiceTreeView extends ServiceView { myTreeModel = new ServiceViewTreeModel(model); myTree = new ServiceViewTree(myTreeModel, this); - myListener = new ServiceViewTreeModelListener(); - model.addModelListener(myListener); - ServiceViewActionProvider actionProvider = ServiceViewActionProvider.getInstance(); ui.setServiceToolbar(actionProvider); ui.setMasterComponent(myTree, actionProvider); @@ -86,18 +85,30 @@ final class ServiceTreeView extends ServiceView { myTree.addTreeSelectionListener(new RestoreSelectionListener()); myTree.addTreeSelectionListener(e -> onSelectionChanged()); - Consumer selector = item -> - select(item.getValue(), item.getRootContributor().getClass()) - .onSuccess(result -> AppUIExecutor.onUiThread().expireWith(this).submit(() -> { - JComponent component = getUi().getDetailsComponent(); - if (component != null) { - IdeFocusManager.getInstance(getProject()).requestFocus(component, false); - } - })); - myNavBarPanel = new ServiceViewNavBarPanel(getProject(), true, getModel(), selector); - myNavBarPanel.getModel().updateModel((Object)null); + ServiceViewNavBarSelector selector = new ServiceViewNavBarSelector() { + @Override + public void select(@NotNull ServiceModel.ServiceViewItem item) { + ServiceTreeView.this.select(item.getValue(), item.getRootContributor().getClass()) + .onSuccess(result -> AppUIExecutor.onUiThread().expireWith(ServiceTreeView.this).submit(() -> { + JComponent component = getUi().getDetailsComponent(); + if (component != null) { + IdeFocusManager.getInstance(getProject()).requestFocus(component, false); + } + })); + } + + @Nullable + @Override + public ServiceViewItem getSelectedItem() { + return myLastSelection; + } + }; + myNavBarPanel = ServiceViewNavBarService.getInstance(project).createNavBarPanel(this, selector); myUi.setNavBar(myNavBarPanel); + myListener = new ServiceViewTreeModelListener(); + model.addModelListener(myListener); + if (model instanceof ServiceViewModel.AllServicesModel) { setEmptyText(myTree, myTree.getEmptyText()); } @@ -241,7 +252,11 @@ final class ServiceTreeView extends ServiceView { IdeFocusManager.getInstance(getProject()).requestFocus(myTree, false); } else { - myNavBarPanel.rebuildAndSelectTail(true); + NavBarVm vm = myNavBarPanel.getModel(); + if (vm != null) { + myNavBarPanel.updateModel(); + vm.selectTail(); + } } } @@ -271,7 +286,7 @@ final class ServiceTreeView extends ServiceView { } myLastSelection = newSelection; - myNavBarPanel.getModel().updateModel(newSelection); + myNavBarPanel.updateModel(); if (!mySelected) return; @@ -325,40 +340,6 @@ final class ServiceTreeView extends ServiceView { }); } - private void updateNavBar() { - AppUIExecutor.onUiThread().expireWith(this).submit(() -> { - ServiceViewItem item = getNavBarItem(); - if (item == null) return; - - WeakReference itemRef = new WeakReference<>(item); - getModel().getInvoker().invoke(() -> { - ServiceViewItem viewItem = itemRef.get(); - if (viewItem == null) return; - - ServiceViewItem updatedItem = getModel().findItemSafe(viewItem); - if (updatedItem != null) { - WeakReference updatedRef = new WeakReference<>(updatedItem); - AppUIExecutor.onUiThread().expireWith(this).submit(() -> { - ServiceViewItem updatedViewItem = updatedRef.get(); - if (updatedViewItem == null) return; - - ServiceViewItem navBarItem = getNavBarItem(); - if (updatedViewItem.equals(navBarItem) && !updatedViewItem.isRemoved()) { - myNavBarPanel.getModel().updateModel(updatedItem); - } - }); - } - }); - }); - } - - private ServiceViewItem getNavBarItem() { - NavBarModel navBarModel = myNavBarPanel.getModel(); - if (navBarModel.isEmpty()) return null; - - return ObjectUtils.tryCast(navBarModel.getElement(navBarModel.size() - 1), ServiceViewItem.class); - } - private void updateSelectionPaths() { AppUIExecutor.onUiThread().expireWith(this).submit(() -> { TreePath[] currentPaths = myTree.getSelectionPaths(); @@ -558,11 +539,7 @@ final class ServiceTreeView extends ServiceView { } private void updateNavBar() { - AppUIExecutor.onUiThread().expireWith(myNavBarPanel).submit(() -> { - myNavBarPanel.hidePopup(); - myNavBarPanel.getModel().updateModel((Object)null); - myNavBarPanel.getUpdateQueue().rebuildUi(); - }); + myNavBarPanel.updateModel(); } } diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarExtension.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarExtension.java index 9f810ad848e3..1522d6e06c04 100644 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarExtension.java +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarExtension.java @@ -2,14 +2,19 @@ package com.intellij.execution.services; import com.intellij.execution.services.ServiceModel.ServiceViewItem; -import com.intellij.execution.services.ServiceViewNavBarPanel.ServiceViewNavBarRoot; import com.intellij.icons.AllIcons; +import com.intellij.ide.navbar.impl.DefaultNavBarItem; import com.intellij.ide.navigationToolbar.AbstractNavBarModelExtension; +import com.intellij.ide.util.treeView.WeighedItem; import com.intellij.openapi.actionSystem.DataProvider; +import com.intellij.openapi.actionSystem.PlatformCoreDataKeys; +import com.intellij.util.Processor; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; +import java.util.List; public class ServiceViewNavBarExtension extends AbstractNavBarModelExtension { @Nullable @@ -19,11 +24,36 @@ public class ServiceViewNavBarExtension extends AbstractNavBarModelExtension { return serviceView == null ? null : ServiceViewActionProvider.SERVICE_VIEW_ITEM_POPUP; } + @Override + public @Nullable Object getData(@NotNull String dataId, @NotNull DataProvider provider) { + if (PlatformCoreDataKeys.SELECTED_ITEM.is(dataId)) { + return unwrapNavBarItem(PlatformCoreDataKeys.SELECTED_ITEM.getData(provider)); + } + if (PlatformCoreDataKeys.SELECTED_ITEMS.is(dataId)) { + Object[] items = PlatformCoreDataKeys.SELECTED_ITEMS.getData(provider); + if (items != null) { + return ContainerUtil.map2Array(items, ServiceViewNavBarExtension::unwrapNavBarItem); + } + return null; + } + return null; + } + + private static Object unwrapNavBarItem(Object item) { + if (item instanceof DefaultNavBarItem defaultNavBarItem) { + Object data = defaultNavBarItem.getData(); + if (data instanceof ServiceViewNavBarItem serviceViewNavBarItem) { + return serviceViewNavBarItem.getItem().getValue(); + } + } + return item; + } + @Nullable @Override public String getPresentableText(Object object) { - if (object instanceof ServiceViewItem) { - return ServiceViewDragHelper.getDisplayName(((ServiceViewItem)object).getViewDescriptor().getPresentation()); + if (object instanceof ServiceViewNavBarItem item) { + return ServiceViewDragHelper.getDisplayName(item.getItem().getViewDescriptor().getPresentation()); } if (object instanceof ServiceViewNavBarRoot) { return ""; @@ -34,12 +64,81 @@ public class ServiceViewNavBarExtension extends AbstractNavBarModelExtension { @Nullable @Override public Icon getIcon(Object object) { - if (object instanceof ServiceViewItem) { - return ((ServiceViewItem)object).getViewDescriptor().getPresentation().getIcon(false); + if (object instanceof ServiceViewNavBarItem item) { + return item.getItem().getViewDescriptor().getPresentation().getIcon(false); } if (object instanceof ServiceViewNavBarRoot) { return AllIcons.Nodes.Services; } return null; } + + @Override + public boolean processChildren(Object object, Object rootElement, Processor processor) { + if (object instanceof ServiceViewNavBarRoot root) { + ServiceViewModel viewModel = root.getViewModel(); + + List roots = viewModel.getVisibleRoots(); + for (int i = 0; i < roots.size(); i++) { + if (!processor.process(new ServiceViewNavBarItem(roots.get(i), viewModel, -i))) return false; + } + return true; + } + if (object instanceof ServiceViewNavBarItem navBarItem) { + ServiceViewModel viewModel = navBarItem.getViewModel(); + ServiceViewItem item = navBarItem.getItem(); + + if (item instanceof ServiceModel.ServiceNode service) { + if (service.getProvidingContributor() != null && !service.isChildrenInitialized()) { + viewModel.getInvoker().invoke(() -> { + service.getChildren(); // initialize children on background thread + }); + return true; + } + } + List children = viewModel.getChildren(item); + for (int i = 0; i < children.size(); i++) { + if (!processor.process(new ServiceViewNavBarItem(children.get(i), viewModel, -i))) return false; + } + return true; + } + return true; + } + + static final class ServiceViewNavBarRoot { + private final ServiceViewModel myViewModel; + + ServiceViewNavBarRoot(@NotNull ServiceViewModel viewModel) { + myViewModel = viewModel; + } + + @NotNull ServiceViewModel getViewModel() { + return myViewModel; + } + } + + static final class ServiceViewNavBarItem implements WeighedItem { + private final ServiceViewItem myItem; + private final ServiceViewModel myViewModel; + private final int myWeight; + + ServiceViewNavBarItem(@NotNull ServiceViewItem item, @NotNull ServiceViewModel viewModel, int weight) { + myItem = item; + myViewModel = viewModel; + myWeight = weight; + } + + @Override + public int getWeight() { + return myWeight; + } + + @NotNull ServiceViewItem getItem() { + return myItem; + } + + @NotNull ServiceViewModel getViewModel() { + return myViewModel; + } + } } diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarPanel.java b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarPanel.java deleted file mode 100644 index ed2ddd862bc4..000000000000 --- a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarPanel.java +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. -package com.intellij.execution.services; - -import com.intellij.execution.services.ServiceModel.ServiceNode; -import com.intellij.execution.services.ServiceModel.ServiceViewItem; -import com.intellij.ide.navigationToolbar.NavBarModel; -import com.intellij.ide.navigationToolbar.NavBarModelExtension; -import com.intellij.ide.navigationToolbar.NavBarModelListener; -import com.intellij.ide.navigationToolbar.NavBarPanel; -import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Disposer; -import com.intellij.psi.PsiElement; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.function.Consumer; - -final class ServiceViewNavBarPanel extends NavBarPanel { - private final @NotNull Consumer mySelector; - private boolean myRebuildNeeded = true; - - ServiceViewNavBarPanel(@NotNull Project project, boolean docked, @NotNull ServiceViewModel viewModel, - @NotNull Consumer selector) { - super(project, docked); - mySelector = selector; - Disposer.register(viewModel, this); - ((ServiceViewNavBarModel)getModel()).setViewModel(viewModel); - } - - @Override - protected boolean isDisposeOnRemove() { - return false; - } - - @Override - protected NavBarModel createModel() { - return new ServiceViewNavBarModel(myProject, new NavBarModelListener() { - @Override - public void modelChanged() { - myRebuildNeeded = true; - getUpdateQueue().queueRebuildUi(); - } - - @Override - public void selectionChanged() { - updateItems(); - scrollSelectionToVisible(true); - } - }); - } - - @Override - public boolean isRebuildUiNeeded() { - if (myRebuildNeeded) { - myRebuildNeeded = false; - return true; - } - return super.isRebuildUiNeeded(); - } - - @Override - protected void doubleClick(Object object) { - if (object instanceof ServiceViewItem) { - mySelector.accept((ServiceViewItem)object); - } - hideHint(true); - } - - @Override - public @Nullable Object getData(@NotNull String dataId) { - if (NAV_BAR_ACTION_HANDLER.is(dataId)) { - return this; - } - return super.getData(dataId); - } - - void hidePopup() { - hideHint(false); - } - - private static final class ServiceViewNavBarModel extends NavBarModel { - private ServiceViewModel myViewModel; - private final ServiceViewNavBarRoot myRoot = new ServiceViewNavBarRoot(); - - ServiceViewNavBarModel(@NotNull Project project, @NotNull NavBarModelListener notificator) { - super(project, notificator, null); - } - - void setViewModel(@NotNull ServiceViewModel viewModel) { - myViewModel = viewModel; - } - - @Override - public void updateModel(DataContext dataContext) { - } - - @Override - public void updateModelAsync(@NotNull DataContext dataContext, @Nullable Runnable callback) { - } - - @Override - protected void updateModel(PsiElement psiElement, NavBarModelExtension ownerExtension) { - } - - @Override - public void updateModel(Object object) { - List path = new ArrayList<>(); - if (object instanceof ServiceViewItem item) { - List roots = myViewModel.getVisibleRoots(); - - do { - path.add(item); - item = roots.contains(item) ? null : item.getParent(); - } - while (item != null); - } - path.add(myRoot); - Collections.reverse(path); - setModel(path, true); - } - - @Override - protected boolean hasChildren(Object object) { - return !getChildren(object).isEmpty(); - } - - @Override - public List getChildren(Object object) { - if (object == myRoot) { - return new ArrayList<>(myViewModel.getVisibleRoots()); - } - if (object instanceof ServiceViewItem) { - if (object instanceof ServiceNode service) { - if (service.getProvidingContributor() != null && !service.isChildrenInitialized()) { - myViewModel.getInvoker().invoke(() -> { - service.getChildren(); // initialize children on background thread - }); - return Collections.emptyList(); - } - } - return new ArrayList<>(myViewModel.getChildren((ServiceViewItem)object)); - } - return Collections.emptyList(); - } - } - - static final class ServiceViewNavBarRoot { - } -} diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarPanel.kt b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarPanel.kt new file mode 100644 index 000000000000..8639eaa63902 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarPanel.kt @@ -0,0 +1,99 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.services + +import com.intellij.execution.services.ServiceViewNavBarExtension.ServiceViewNavBarItem +import com.intellij.execution.services.ServiceViewNavBarService.ServiceViewNavBarSelector +import com.intellij.ide.navbar.NavBarItem +import com.intellij.ide.navbar.ide.NavBarVmItem +import com.intellij.ide.navbar.ide.toVmItems +import com.intellij.ide.navbar.impl.DefaultNavBarItem +import com.intellij.ide.navbar.ui.StaticNavBarPanel +import com.intellij.openapi.application.readAction +import com.intellij.openapi.project.Project +import com.intellij.util.ui.update.Activatable +import com.intellij.util.ui.update.UiNotifyConnector +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.channels.awaitClose +import kotlinx.coroutines.flow.* +import kotlinx.coroutines.launch +import java.awt.Window +import java.util.function.Supplier + +internal class ServiceViewNavBarPanel(project: Project, + cs: CoroutineScope, + private val updateRequests: MutableSharedFlow, + private val viewModel: ServiceViewModel, + private val selector: ServiceViewNavBarSelector, + requestNavigation: (NavBarVmItem) -> Unit) : + StaticNavBarPanel(project, cs, updateRequests, requestNavigation) { + private val root = ServiceViewNavBarExtension.ServiceViewNavBarRoot(viewModel) + @Volatile + private var visible = true + private val listeners = ArrayList>() + + init { + UiNotifyConnector.installOn(this, object : Activatable { + override fun showNotify() { + visible = true + } + + override fun hideNotify() { + visible = false + notifyListeners() + } + }, false) + + cs.launch { + channelFlow { + listeners.add { + trySend(Unit) + } + awaitClose() + } + .buffer(Channel.CONFLATED) + .collect(updateRequests) + } + } + + fun updateModel() { + if (visible) { + notifyListeners() + } + } + + private fun notifyListeners() { + for (listener in listeners) { + listener.get() + } + } + + override suspend fun getDefaultModel(project: Project): List { + return readAction { getItems().toVmItems() } + } + + override fun contextItems(window: Window): Flow> { + @OptIn(ExperimentalCoroutinesApi::class) + return updateRequests.transformLatest { + val path = getItems() + emit(readAction { path.toVmItems() }) + } + } + + private fun getItems(): List { + val path = ArrayList() + var item = if (visible) selector.getSelectedItem() else null + if (item != null) { + val roots = viewModel.visibleRoots + do { + path.add(DefaultNavBarItem(ServiceViewNavBarItem(item!!, viewModel, 0))) + item = if (roots.contains(item)) null else item.parent + } + while (item != null) + } + path.add(DefaultNavBarItem(root)) + path.reverse() + return path + } +} \ No newline at end of file diff --git a/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarService.kt b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarService.kt new file mode 100644 index 000000000000..fecc469e16b0 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/execution/services/ServiceViewNavBarService.kt @@ -0,0 +1,49 @@ +// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. +package com.intellij.execution.services + +import com.intellij.execution.services.ServiceModel.ServiceViewItem +import com.intellij.execution.services.ServiceViewNavBarExtension.ServiceViewNavBarItem +import com.intellij.ide.navbar.impl.DefaultNavBarItem +import com.intellij.openapi.application.EDT +import com.intellij.openapi.components.Service +import com.intellij.openapi.components.service +import com.intellij.openapi.project.Project +import com.intellij.openapi.util.Disposer +import com.intellij.util.childScope +import kotlinx.coroutines.* +import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.MutableSharedFlow + +@Service(Service.Level.PROJECT) +internal class ServiceViewNavBarService(val project: Project, val cs: CoroutineScope) { + companion object { + @JvmStatic + fun getInstance(project: Project): ServiceViewNavBarService = project.service() + } + + fun createNavBarPanel(serviceView: ServiceView, + selector: ServiceViewNavBarSelector): ServiceViewNavBarPanel { + @OptIn(ExperimentalCoroutinesApi::class) + val childScope = cs.childScope(Dispatchers.Default.limitedParallelism(1)) + Disposer.register(serviceView) { + childScope.cancel() + } + + val updateRequests: MutableSharedFlow = MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST) + + return ServiceViewNavBarPanel(project, childScope, updateRequests, serviceView.model, selector) { + childScope.launch { + val item = ((it.pointer.dereference() as? DefaultNavBarItem<*>)?.data as? ServiceViewNavBarItem)?.item ?: return@launch + withContext(Dispatchers.EDT) { + selector.select(item) + } + } + } + } + + internal interface ServiceViewNavBarSelector { + fun select(item: ServiceViewItem) + + fun getSelectedItem(): ServiceViewItem? + } +} diff --git a/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItem.kt b/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItem.kt index ad1c3c481789..00f18fe21e07 100644 --- a/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItem.kt +++ b/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItem.kt @@ -7,6 +7,7 @@ import com.intellij.ide.navbar.NavBarItemPresentation import com.intellij.ide.navigationToolbar.NavBarModelExtension import com.intellij.ide.projectView.ProjectView import com.intellij.ide.projectView.impl.ProjectRootsUtil +import com.intellij.ide.util.treeView.WeighedItem import com.intellij.model.Pointer import com.intellij.model.Pointer.hardPointer import com.intellij.openapi.editor.colors.CodeInsightColors.ERRORS_ATTRIBUTES @@ -213,6 +214,10 @@ internal class OrderEntryNavBarItem(data: OrderEntry) : DefaultNavBarItem(data) { + override fun weight(): Int = data.weight +} + @Internal val navBarErrorAttributes: SimpleTextAttributes = EditorColorsManager.getInstance() diff --git a/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItemProvider.kt b/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItemProvider.kt index da6b0dc603a0..dc3597ee8742 100644 --- a/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItemProvider.kt +++ b/platform/lang-impl/src/com/intellij/ide/navbar/impl/DefaultNavBarItemProvider.kt @@ -5,6 +5,7 @@ import com.intellij.diagnostic.PluginException import com.intellij.ide.navbar.NavBarItem import com.intellij.ide.navbar.NavBarItemProvider import com.intellij.ide.navigationToolbar.NavBarModelExtension +import com.intellij.ide.util.treeView.WeighedItem import com.intellij.openapi.module.Module import com.intellij.openapi.progress.ProcessCanceledException import com.intellij.openapi.progress.ProgressManager @@ -143,6 +144,7 @@ class DefaultNavBarItemProvider : NavBarItemProvider { } } is OrderEntry -> OrderEntryNavBarItem(child) + is WeighedItem -> WeightedNavBarItem(child) else -> DefaultNavBarItem(child) } } diff --git a/platform/lang-impl/src/com/intellij/ide/navbar/ui/StaticNavBarPanel.kt b/platform/lang-impl/src/com/intellij/ide/navbar/ui/StaticNavBarPanel.kt index 796e0aa9f9f0..d1f6520d6a83 100644 --- a/platform/lang-impl/src/com/intellij/ide/navbar/ui/StaticNavBarPanel.kt +++ b/platform/lang-impl/src/com/intellij/ide/navbar/ui/StaticNavBarPanel.kt @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.transformLatest import java.awt.BorderLayout import java.awt.Window -internal class StaticNavBarPanel( +internal open class StaticNavBarPanel( private val project: Project, private val cs: CoroutineScope, private val updateRequests: Flow, @@ -70,7 +70,7 @@ internal class StaticNavBarPanel( private suspend fun handleWindow(window: Window): Nothing = supervisorScope { val vm = NavBarVmImpl( this@supervisorScope, - initialItems = defaultModel(project), + initialItems = getDefaultModel(project), contextItems = contextItems(window), requestNavigation, ) @@ -83,7 +83,9 @@ internal class StaticNavBarPanel( } } - private fun contextItems(window: Window): Flow> { + protected open suspend fun getDefaultModel(project: Project): List = defaultModel(project) + + protected open fun contextItems(window: Window): Flow> { @OptIn(ExperimentalCoroutinesApi::class) return updateRequests.transformLatest { dataContext(window, panel = this@StaticNavBarPanel)?.let {