mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
sdk-popup - correctly extract selected element from the popup
GitOrigin-RevId: a9b2310b2e44561fed7311158ff1544911a1f32e
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2c34ddf4c9
commit
98b1e28cc4
+3
-4
@@ -12,9 +12,7 @@ import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkModel;
|
||||
import com.intellij.openapi.projectRoots.impl.ProjectJdkImpl;
|
||||
import com.intellij.openapi.roots.ProjectRootManager;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkListModelBuilder;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkPopupFactory;
|
||||
import com.intellij.openapi.roots.ui.configuration.ProjectStructureConfigurable;
|
||||
import com.intellij.openapi.roots.ui.configuration.*;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.ProjectStructureElement;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.daemon.SdkProjectStructureElement;
|
||||
import com.intellij.openapi.ui.MasterDetailsComponent;
|
||||
@@ -238,7 +236,8 @@ public class JdkListConfigurable extends BaseStructureConfigurable {
|
||||
notSimpleJavaSdkType(),
|
||||
null,
|
||||
sdk -> false)
|
||||
).showPopup(e, () -> {});
|
||||
).createPopup(new SdkPopup.SdkPopupListener() { })
|
||||
.showPopup(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+20
-19
@@ -14,12 +14,17 @@ import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.progress.Task;
|
||||
import com.intellij.openapi.progress.util.ProgressIndicatorBase;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.projectRoots.*;
|
||||
import com.intellij.openapi.projectRoots.ProjectJdkTable;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.openapi.projectRoots.SdkModificator;
|
||||
import com.intellij.openapi.projectRoots.SdkType;
|
||||
import com.intellij.openapi.projectRoots.impl.SdkUsagesCollector.SdkUsage;
|
||||
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.DownloadSdkFix;
|
||||
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.LocalSdkFix;
|
||||
import com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.UnknownSdkLookup;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkListItem;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkListModelBuilder;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkPopup;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkPopupFactory;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.SdkDownloadTask;
|
||||
@@ -36,7 +41,6 @@ import org.jetbrains.annotations.Nullable;
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static com.intellij.openapi.progress.PerformInBackgroundOption.ALWAYS_BACKGROUND;
|
||||
import static com.intellij.openapi.projectRoots.impl.UnknownSdkResolver.UnknownSdk;
|
||||
@@ -212,25 +216,22 @@ public class UnknownSdkTracker {
|
||||
modelBuilder
|
||||
);
|
||||
|
||||
AtomicReference<Sdk> wasSdkCreated = new AtomicReference<>(null);
|
||||
model.addListener(new SdkModel.Listener() {
|
||||
@Override
|
||||
public void sdkAdded(@NotNull Sdk sdk) {
|
||||
//TODO: handle if an existing item is selected!
|
||||
//it is easier and safer than committing the ProjectSdksModel instance
|
||||
wasSdkCreated.set(sdk);
|
||||
}
|
||||
});
|
||||
|
||||
popup.showUnderneathToTheRightOf(
|
||||
underneathRightOfComponent,
|
||||
() -> {
|
||||
Sdk sdk = wasSdkCreated.get();
|
||||
if (sdk != null) {
|
||||
onSelectionMade.consume(sdk);
|
||||
popup.createPopup(new SdkPopup.SdkPopupListener() {
|
||||
private void handleNewItem(@NotNull SdkListItem item) {
|
||||
if (item instanceof SdkListItem.SdkItem) {
|
||||
onSelectionMade.consume(((SdkListItem.SdkItem)item).getSdk());
|
||||
}
|
||||
}
|
||||
);
|
||||
@Override
|
||||
public void onNewItemAdded(@NotNull SdkListItem item) {
|
||||
handleNewItem(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onExistingItemSelected(@NotNull SdkListItem item) {
|
||||
handleNewItem(item);
|
||||
}
|
||||
}).showUnderneathToTheRightOf(underneathRightOfComponent);
|
||||
}
|
||||
|
||||
private static void configureLocalSdks(@NotNull Map<UnknownSdk, LocalSdkFix> localFixes) {
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2000-2019 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.openapi.roots.ui.configuration;
|
||||
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface SdkPopup {
|
||||
void showPopup(@NotNull AnActionEvent e);
|
||||
void showUnderneathToTheRightOf(@NotNull Component component);
|
||||
|
||||
interface SdkPopupListener {
|
||||
/**
|
||||
* Executed on popup is closed, independently from the result
|
||||
*/
|
||||
default void onClosed() {}
|
||||
|
||||
/**
|
||||
* Executed when a new item was created via a user action
|
||||
* and added to the model, called after model is refreshed
|
||||
*/
|
||||
default void onNewItemAdded(@NotNull SdkListItem item) {}
|
||||
|
||||
/**
|
||||
* Executed when an existing selectable item was selected
|
||||
* in the popup, it does mean no new items were created
|
||||
* by a user
|
||||
*/
|
||||
default void onExistingItemSelected(@NotNull SdkListItem item) {}
|
||||
}
|
||||
}
|
||||
+36
-36
@@ -4,6 +4,7 @@ package com.intellij.openapi.roots.ui.configuration;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkListModelBuilder.ModelListener;
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkPopup.SdkPopupListener;
|
||||
import com.intellij.openapi.roots.ui.configuration.projectRoot.ProjectSdksModel;
|
||||
import com.intellij.openapi.ui.popup.JBPopupListener;
|
||||
import com.intellij.openapi.ui.popup.LightweightWindowEvent;
|
||||
@@ -16,9 +17,10 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SdkPopupFactory {
|
||||
private final SdkListModelBuilder myModel;
|
||||
private final SdkListModelBuilder myModelBuilder;
|
||||
@Nullable private final Project myProject;
|
||||
@NotNull private final ProjectSdksModel mySdkModel;
|
||||
|
||||
@@ -27,13 +29,21 @@ public class SdkPopupFactory {
|
||||
@NotNull SdkListModelBuilder modelBuilder) {
|
||||
myProject = project;
|
||||
mySdkModel = sdkModel;
|
||||
myModel = modelBuilder;
|
||||
myModelBuilder = modelBuilder;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ComboBoxPopup<SdkListItem> createPopup(@NotNull Runnable onClosed) {
|
||||
public SdkPopup createPopup(@NotNull SdkPopupListener listener) {
|
||||
SdkListItemContext context = new SdkListItemContext();
|
||||
ComboBoxPopup<SdkListItem> popup = new ComboBoxPopup<>(context, null);
|
||||
|
||||
SdkPopupImpl popup = new SdkPopupImpl(context, value -> {
|
||||
if (value instanceof SdkListItem.ActionableItem) {
|
||||
((SdkListItem.ActionableItem)value).executeAction();
|
||||
//TODO: handle the outcome of the action execution here
|
||||
return;
|
||||
}
|
||||
listener.onExistingItemSelected(value);
|
||||
});
|
||||
|
||||
ModelListener modelListener = new ModelListener() {
|
||||
@Override
|
||||
@@ -42,46 +52,45 @@ public class SdkPopupFactory {
|
||||
popup.syncWithModelChange();
|
||||
}
|
||||
};
|
||||
myModel.addModelListener(modelListener);
|
||||
|
||||
myModelBuilder.addModelListener(modelListener);
|
||||
|
||||
popup.addListener(new JBPopupListener() {
|
||||
@Override
|
||||
public void beforeShown(@NotNull LightweightWindowEvent event) {
|
||||
myModel.reloadActions(popup.getList(), null);
|
||||
myModel.detectItems(popup.getList(), popup);
|
||||
myModelBuilder.reloadActions(popup.getList(), null);
|
||||
myModelBuilder.detectItems(popup.getList(), popup);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClosed(@NotNull LightweightWindowEvent event) {
|
||||
myModel.removeListener(modelListener);
|
||||
onClosed.run();
|
||||
myModelBuilder.removeListener(modelListener);
|
||||
listener.onClosed();
|
||||
}
|
||||
});
|
||||
|
||||
return popup;
|
||||
}
|
||||
|
||||
public void showPopup(@NotNull AnActionEvent e,
|
||||
@NotNull Runnable onClosed) {
|
||||
ComboBoxPopup<SdkListItem> popup = createPopup(onClosed);
|
||||
|
||||
if (e instanceof AnActionButton.AnActionEventWrapper) {
|
||||
((AnActionButton.AnActionEventWrapper)e).showPopup(popup);
|
||||
} else {
|
||||
popup.showInBestPositionFor(e.getDataContext());
|
||||
private class SdkPopupImpl extends ComboBoxPopup<SdkListItem> implements SdkPopup {
|
||||
SdkPopupImpl(SdkListItemContext context, Consumer<SdkListItem> onItemSelected) {
|
||||
super(context, null, onItemSelected);
|
||||
}
|
||||
}
|
||||
|
||||
public void showPopup(@NotNull RelativePoint aPoint,
|
||||
@NotNull Runnable onClosed) {
|
||||
createPopup(onClosed).show(aPoint);
|
||||
}
|
||||
@Override
|
||||
public void showPopup(@NotNull AnActionEvent e) {
|
||||
if (e instanceof AnActionButton.AnActionEventWrapper) {
|
||||
((AnActionButton.AnActionEventWrapper)e).showPopup(this);
|
||||
} else {
|
||||
showInBestPositionFor(e.getDataContext());
|
||||
}
|
||||
}
|
||||
|
||||
public void showUnderneathToTheRightOf(@NotNull Component component,
|
||||
@NotNull Runnable onClosed) {
|
||||
ComboBoxPopup<SdkListItem> popup = createPopup(onClosed);
|
||||
int popupWidth = popup.getList().getPreferredSize().width;
|
||||
popup.show(new RelativePoint(component, new Point(component.getWidth() - popupWidth, component.getHeight())));
|
||||
@Override
|
||||
public void showUnderneathToTheRightOf(@NotNull Component component) {
|
||||
int popupWidth = getList().getPreferredSize().width;
|
||||
show(new RelativePoint(component, new Point(component.getWidth() - popupWidth, component.getHeight())));
|
||||
}
|
||||
}
|
||||
|
||||
private class SdkListItemContext implements ComboBoxPopup.Context<SdkListItem> {
|
||||
@@ -120,14 +129,5 @@ public class SdkPopupFactory {
|
||||
public ListCellRenderer<? super SdkListItem> getRenderer() {
|
||||
return myRenderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedItem(SdkListItem value) {
|
||||
if (value != null) {
|
||||
if (value instanceof SdkListItem.ActionableItem) {
|
||||
((SdkListItem.ActionableItem)value).executeAction();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-9
@@ -66,14 +66,6 @@ public class DarculaJBPopupComboPopup<T> implements ComboPopup, ComboBoxPopup.Co
|
||||
return myComboBox.getRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelectedItem(T selectedValue) {
|
||||
// this call could show some more controls...
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
myComboBox.setSelectedItem(selectedValue);
|
||||
}, ModalityState.stateForComponent(myComboBox));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaximumRowCount() {
|
||||
return Math.max(10, myComboBox.getMaximumRowCount());
|
||||
@@ -93,7 +85,9 @@ public class DarculaJBPopupComboPopup<T> implements ComboPopup, ComboBoxPopup.Co
|
||||
myPopup.cancel();
|
||||
}
|
||||
|
||||
myPopup = new ComboBoxPopup<T>(this, myComboBox.getSelectedItem()) {
|
||||
//noinspection unchecked
|
||||
T selectedItem = (T)myComboBox.getSelectedItem();
|
||||
myPopup = new ComboBoxPopup<T>(this, selectedItem, value -> myComboBox.setSelectedItem(value)) {
|
||||
@Override
|
||||
public void cancel(InputEvent e) {
|
||||
if (e instanceof MouseEvent) {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2000-2019 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.ui.popup.list;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.ComboBoxPopupState;
|
||||
import com.intellij.openapi.ui.popup.PopupStep;
|
||||
@@ -9,6 +10,7 @@ import com.intellij.ui.SimpleColoredComponent;
|
||||
import com.intellij.ui.TitledSeparator;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.ui.popup.WizardPopup;
|
||||
import java.util.function.Consumer;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -17,14 +19,17 @@ import javax.swing.*;
|
||||
import javax.swing.border.Border;
|
||||
import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EventListener;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
private final Context<T> myContext;
|
||||
|
||||
public ComboBoxPopup(@NotNull Context<T> context, @Nullable Object selectedItem) {
|
||||
this(context, null, popupStateFromContext(context, selectedItem), null);
|
||||
public ComboBoxPopup(@NotNull Context<T> context,
|
||||
@Nullable T selectedItem,
|
||||
@NotNull Consumer<T> onItemSelected) {
|
||||
this(context, null, popupStateFromContext(context, onItemSelected, selectedItem), null);
|
||||
}
|
||||
|
||||
private ComboBoxPopup(@NotNull Context<T> context,
|
||||
@@ -38,8 +43,11 @@ public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
|
||||
@NotNull
|
||||
private static <T> MyBasePopupState<T> popupStateFromContext(@NotNull Context<T> context,
|
||||
@NotNull Consumer<T> onItemSelected,
|
||||
@Nullable Object selectedItem) {
|
||||
MyBasePopupState<T> step = new MyBasePopupState<T>(context, () -> context.getModel()) {
|
||||
MyBasePopupState<T> step = new MyBasePopupState<T>(onItemSelected,
|
||||
() -> context.getModel(),
|
||||
() -> context.getRenderer()) {
|
||||
@Override
|
||||
public void canceled() {
|
||||
context.onPopupStepCancelled();
|
||||
@@ -63,14 +71,16 @@ public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
@NotNull
|
||||
ListCellRenderer<? super T> getRenderer();
|
||||
|
||||
void setSelectedItem(T value);
|
||||
|
||||
default int getMaximumRowCount() { return 10; }
|
||||
default void onPopupStepCancelled() {}
|
||||
default void configureList(@NotNull JList<T> list) {}
|
||||
default void customizeListRendererComponent(JComponent component) {}
|
||||
}
|
||||
|
||||
public interface SelectionListener<T> extends EventListener {
|
||||
void setSelectedItem(@NotNull T value);
|
||||
}
|
||||
|
||||
public void syncWithModelChange() {
|
||||
//mouse may be under the expandable item, sub-popup would be shown,
|
||||
//this popup could grow/shrink making the sub-popup mispositioned,
|
||||
@@ -163,14 +173,17 @@ public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
|
||||
private static class MyBasePopupState<T> extends BaseListPopupStep<T> {
|
||||
private final JBList<T> myProxyList = new JBList<>();
|
||||
private final Context<T> myContext;
|
||||
private final Consumer<T> myOnItemSelected;
|
||||
private final Supplier<ListModel<T>> myGetComboboxModel;
|
||||
private final Supplier<ListCellRenderer<? super T>> myGetRenderer;
|
||||
|
||||
private MyBasePopupState(@NotNull Context<T> context,
|
||||
@NotNull Supplier<ListModel<T>> getComboboxModel) {
|
||||
private MyBasePopupState(@NotNull Consumer<T> onItemSelected,
|
||||
@NotNull Supplier<ListModel<T>> getComboboxModel,
|
||||
@NotNull Supplier<ListCellRenderer<? super T>> getRenderer) {
|
||||
super(null, copyItemsFromModel(getComboboxModel.get()));
|
||||
myOnItemSelected = onItemSelected;
|
||||
myGetComboboxModel = getComboboxModel;
|
||||
myContext = context;
|
||||
myGetRenderer = getRenderer;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -182,11 +195,14 @@ public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
//noinspection unchecked
|
||||
ListModel<T> nextModel = ((ComboBoxPopupState<T>)model).onChosen(selectedValue);
|
||||
if (nextModel != null) {
|
||||
return new MyBasePopupState<>(myContext, () -> nextModel);
|
||||
return new MyBasePopupState<>(myOnItemSelected, () -> nextModel, myGetRenderer);
|
||||
}
|
||||
}
|
||||
|
||||
myContext.setSelectedItem(selectedValue);
|
||||
if (selectedValue != null) {
|
||||
ApplicationManager.getApplication().invokeLater(() -> myOnItemSelected.accept(selectedValue));
|
||||
}
|
||||
|
||||
return FINAL_CHOICE;
|
||||
}
|
||||
|
||||
@@ -208,7 +224,7 @@ public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
@NotNull
|
||||
@Override
|
||||
public String getTextFor(T value) {
|
||||
Component component = myContext.getRenderer().getListCellRendererComponent(myProxyList, value, -1, false, false);
|
||||
Component component = myGetRenderer.get().getListCellRendererComponent(myProxyList, value, -1, false, false);
|
||||
return component instanceof TitledSeparator || component instanceof JSeparator ? "" :
|
||||
component instanceof JLabel ? ((JLabel)component).getText() :
|
||||
component instanceof SimpleColoredComponent
|
||||
@@ -218,7 +234,7 @@ public class ComboBoxPopup<T> extends ListPopupImpl {
|
||||
|
||||
@Override
|
||||
public boolean isSelectable(T value) {
|
||||
Component component = myContext.getRenderer().getListCellRendererComponent(myProxyList, value, -1, false, false);
|
||||
Component component = myGetRenderer.get().getListCellRendererComponent(myProxyList, value, -1, false, false);
|
||||
return !(component instanceof TitledSeparator || component instanceof JSeparator);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user