diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroup.java index 1412fce51472..a21f3fe98eba 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroup.java @@ -16,9 +16,45 @@ package com.intellij.dvcs.ui; import com.intellij.openapi.actionSystem.ActionGroup; +import com.intellij.openapi.project.DumbAware; +import com.intellij.ui.LayeredIcon; +import com.intellij.util.ui.EmptyIcon; + +import static icons.DvcsImplIcons.*; + +public abstract class BranchActionGroup extends ActionGroup implements DumbAware { + + private boolean myIsFavourite; + private final LayeredIcon myIcon; + private final LayeredIcon myHoveredIcon; -public abstract class BranchActionGroup extends ActionGroup { public BranchActionGroup() { super("", true); + myIcon = new LayeredIcon(Favorite, EmptyIcon.ICON_16); + myHoveredIcon = new LayeredIcon(FavoriteOnHover, NotFavoriteOnHover); + getTemplatePresentation().setIcon(myIcon); + getTemplatePresentation().setHoveredIcon(myHoveredIcon); + updateIcons(); + } + + private void updateIcons() { + myIcon.setLayerEnabled(0, myIsFavourite); + myHoveredIcon.setLayerEnabled(0, myIsFavourite); + + myIcon.setLayerEnabled(1, !myIsFavourite); + myHoveredIcon.setLayerEnabled(1, !myIsFavourite); + } + + public boolean isFavourite() { + return myIsFavourite; + } + + public void setFavourite(boolean favourite) { + myIsFavourite = favourite; + updateIcons(); + } + + public void toggle() { + setFavourite(!myIsFavourite); } } diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java index 562740f2dedd..83d5cca07794 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/ui/BranchActionGroupPopup.java @@ -32,6 +32,7 @@ import com.intellij.ui.components.panels.OpaquePanel; import com.intellij.ui.popup.KeepingPopupOpenAction; import com.intellij.ui.popup.PopupFactoryImpl; import com.intellij.ui.popup.WizardPopup; +import com.intellij.ui.popup.list.IconListPopupRenderer; import com.intellij.ui.popup.list.ListPopupImpl; import com.intellij.ui.popup.list.ListPopupModel; import com.intellij.ui.popup.list.PopupListElementRenderer; @@ -44,6 +45,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; import java.awt.*; import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; import java.util.List; import static com.intellij.util.ui.UIUtil.DEFAULT_HGAP; @@ -53,6 +55,7 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { private static final String DIMENSION_SERVICE_KEY = "Vcs.Branch.Popup"; private static final DataKey POPUP_MODEL = DataKey.create("VcsPopupModel"); private int myIgnoreNextItem = 0; + private MyPopupListElementRenderer myListElementRenderer; public BranchActionGroupPopup(@NotNull String title, @NotNull Project project, @NotNull Condition preselectActionCondition, @NotNull ActionGroup actions) { @@ -60,12 +63,14 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { preselectActionCondition, true); setDimensionServiceKey(DIMENSION_SERVICE_KEY); DataManager.registerDataProvider(getList(), dataId -> POPUP_MODEL.is(dataId) ? getListModel() : null); + installOnHoverIconsSupport(getListElementRenderer()); } //for child popups only private BranchActionGroupPopup(@Nullable WizardPopup aParent, @NotNull ListPopupStep aStep, @Nullable Object parentValue) { super(aParent, aStep, DataContext.EMPTY_CONTEXT, parentValue); DataManager.registerDataProvider(getList(), dataId -> POPUP_MODEL.is(dataId) ? getListModel() : null); + installOnHoverIconsSupport(getListElementRenderer()); } @NotNull @@ -98,6 +103,34 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { } } + @Override + public void handleSelect(boolean handleFinalChoices, InputEvent e) { + BranchActionGroup branchActionGroup = getSelectedBranchGroup(); + if (branchActionGroup != null && e instanceof MouseEvent && myListElementRenderer.isIconAt(((MouseEvent)e).getPoint())) { + branchActionGroup.toggle(); + } + else { + super.handleSelect(handleFinalChoices, e); + } + } + + @Override + protected void handleToggleAction() { + BranchActionGroup branchActionGroup = getSelectedBranchGroup(); + if (branchActionGroup != null) { + branchActionGroup.toggle(); + getList().repaint(); + } + else { + super.handleToggleAction(); + } + } + + @Nullable + private BranchActionGroup getSelectedBranchGroup() { + return getSpecificAction(getList().getSelectedValue(), BranchActionGroup.class); + } + @Override protected void onSpeedSearchPatternChanged() { super.onSpeedSearchPatternChanged(); @@ -142,18 +175,26 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { if (clazz.isInstance(action)) { return clazz.cast(action); } + else if (action instanceof MySpeedSearchActionGroup) { + ActionGroup group = ((MySpeedSearchActionGroup)action).getActionGroup(); + return clazz.isInstance(group) ? clazz.cast(group) : null; + } } return null; } @Override - protected ListCellRenderer getListElementRenderer() { - return new MyPopupListElementRenderer(this); + protected MyPopupListElementRenderer getListElementRenderer() { + if (myListElementRenderer == null) { + myListElementRenderer = new MyPopupListElementRenderer(this); + } + return myListElementRenderer; } - private static class MyPopupListElementRenderer extends PopupListElementRenderer { + private class MyPopupListElementRenderer extends PopupListElementRenderer implements IconListPopupRenderer { private ErrorLabel myInfoLabel; + private IconComponent myIconLabel; public MyPopupListElementRenderer(ListPopupImpl aPopup) { super(aPopup); @@ -164,6 +205,18 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { return new MyTextSeparator(); } + @Override + public boolean isIconAt(@NotNull Point point) { + JList list = getList(); + int index = getList().locationToIndex(point); + Rectangle bounds = getList().getCellBounds(index, index); + Component renderer = getListCellRendererComponent(list, list.getSelectedValue(), index, true, true); + renderer.setBounds(bounds); + renderer.doLayout(); + point.translate(-bounds.x, -bounds.y); + return SwingUtilities.getDeepestComponentAt(renderer, point.x, point.y) instanceof IconComponent; + } + @Override protected void customizeComponent(JList list, Object value, boolean isSelected) { MoreAction more = getSpecificAction(value, MoreAction.class); @@ -171,7 +224,9 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { myTextLabel.setForeground(JBColor.gray); } super.customizeComponent(list, value, isSelected); - + myTextLabel.setIcon(null); + myTextLabel.setDisabledIcon(null); + myIconLabel.setIcon(myDescriptor.getIconFor(value)); PopupElementWithAdditionalInfo additionalInfoAction = getSpecificAction(value, PopupElementWithAdditionalInfo.class); String infoText = additionalInfoAction != null ? additionalInfoAction.getInfoText() : null; if (infoText != null) { @@ -202,11 +257,16 @@ public class BranchActionGroupPopup extends FlatSpeedSearchPopup { myInfoLabel.setBorder(JBUI.Borders.empty(1, DEFAULT_HGAP, 1, 1)); JPanel compoundPanel = new OpaquePanel(new BorderLayout(), JBColor.WHITE); + myIconLabel = new IconComponent(); + compoundPanel.add(myIconLabel, BorderLayout.WEST); compoundPanel.add(myTextLabel, BorderLayout.CENTER); compoundPanel.add(myInfoLabel, BorderLayout.EAST); return layoutComponent(compoundPanel); } + + private class IconComponent extends JLabel { + } } private static class MyTextSeparator extends SeparatorWithText { diff --git a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java index beb680063934..b731fa6605cb 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/PopupFactoryImpl.java @@ -423,7 +423,7 @@ public class PopupFactoryImpl extends JBPopupFactory { Rectangle bounds = getList().getCellBounds(index, index); Object selectedValue = getList().getSelectedValue(); if (selectedValue instanceof ActionItem) { - ((ActionItem)selectedValue).setIconHovered(myRenderer.iconContains(point)); + ((ActionItem)selectedValue).setIconHovered(myRenderer.isIconAt(point)); } if (bounds != null) { getList().repaint(bounds); diff --git a/platform/platform-impl/src/com/intellij/ui/popup/list/IconListPopupRenderer.java b/platform/platform-impl/src/com/intellij/ui/popup/list/IconListPopupRenderer.java index 64672d8f6e48..d3bbb81de2cf 100644 --- a/platform/platform-impl/src/com/intellij/ui/popup/list/IconListPopupRenderer.java +++ b/platform/platform-impl/src/com/intellij/ui/popup/list/IconListPopupRenderer.java @@ -21,5 +21,5 @@ import java.awt.*; public interface IconListPopupRenderer { - boolean iconContains(@NotNull Point point); + boolean isIconAt(@NotNull Point point); }