mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[services] IDEA-320784 use new nav bar in Services tool window
GitOrigin-RevId: d43651031472aa4cf301bad233241dbb4d4d9279
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a409d288ed
commit
b47a759d98
@@ -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<ServiceViewItem> 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<ServiceViewItem> itemRef = new WeakReference<>(item);
|
||||
getModel().getInvoker().invoke(() -> {
|
||||
ServiceViewItem viewItem = itemRef.get();
|
||||
if (viewItem == null) return;
|
||||
|
||||
ServiceViewItem updatedItem = getModel().findItemSafe(viewItem);
|
||||
if (updatedItem != null) {
|
||||
WeakReference<ServiceViewItem> 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+104
-5
@@ -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<Object> processor) {
|
||||
if (object instanceof ServiceViewNavBarRoot root) {
|
||||
ServiceViewModel viewModel = root.getViewModel();
|
||||
|
||||
List<? extends ServiceViewItem> 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<? extends ServiceViewItem> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<? super ServiceViewItem> mySelector;
|
||||
private boolean myRebuildNeeded = true;
|
||||
|
||||
ServiceViewNavBarPanel(@NotNull Project project, boolean docked, @NotNull ServiceViewModel viewModel,
|
||||
@NotNull Consumer<? super ServiceViewItem> 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<Object> path = new ArrayList<>();
|
||||
if (object instanceof ServiceViewItem item) {
|
||||
List<? extends ServiceViewItem> 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<Object> 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 {
|
||||
}
|
||||
}
|
||||
@@ -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<Unit>,
|
||||
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<Supplier<Unit>>()
|
||||
|
||||
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<NavBarVmItem> {
|
||||
return readAction { getItems().toVmItems() }
|
||||
}
|
||||
|
||||
override fun contextItems(window: Window): Flow<List<NavBarVmItem>> {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
return updateRequests.transformLatest {
|
||||
val path = getItems()
|
||||
emit(readAction { path.toVmItems() })
|
||||
}
|
||||
}
|
||||
|
||||
private fun getItems(): List<NavBarItem> {
|
||||
val path = ArrayList<NavBarItem>()
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -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<Unit> = 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?
|
||||
}
|
||||
}
|
||||
@@ -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<OrderE
|
||||
}
|
||||
}
|
||||
|
||||
internal class WeightedNavBarItem(data: WeighedItem): DefaultNavBarItem<WeighedItem>(data) {
|
||||
override fun weight(): Int = data.weight
|
||||
}
|
||||
|
||||
@Internal
|
||||
val navBarErrorAttributes: SimpleTextAttributes =
|
||||
EditorColorsManager.getInstance()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Any>,
|
||||
@@ -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<List<NavBarVmItem>> {
|
||||
protected open suspend fun getDefaultModel(project: Project): List<NavBarVmItem> = defaultModel(project)
|
||||
|
||||
protected open fun contextItems(window: Window): Flow<List<NavBarVmItem>> {
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
return updateRequests.transformLatest {
|
||||
dataContext(window, panel = this@StaticNavBarPanel)?.let {
|
||||
|
||||
Reference in New Issue
Block a user