mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[ui]: reuse Icon Wrapper from IconUtil and support onHover icon
* draw an appropriate icon depending on the MouseLocation; * add an interface to detect if mouse position hovers its icon: it can be done properly only with related renderer component; * update Icons if action presentation icon changed; * use hoverFlag instead of manually detecting in paintIcon; * repaint listCell if needed;
This commit is contained in:
@@ -48,15 +48,16 @@ import com.intellij.ui.FocusTrackback;
|
||||
import com.intellij.ui.HintHint;
|
||||
import com.intellij.ui.awt.RelativePoint;
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel;
|
||||
import com.intellij.ui.popup.list.IconListPopupRenderer;
|
||||
import com.intellij.ui.popup.list.ListPopupImpl;
|
||||
import com.intellij.ui.popup.mock.MockConfirmation;
|
||||
import com.intellij.ui.popup.tree.TreePopupImpl;
|
||||
import com.intellij.util.IconUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.WeakHashMap;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.StatusText;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -69,14 +70,17 @@ import javax.swing.event.HyperlinkListener;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.InputEvent;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.*;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static com.intellij.openapi.actionSystem.Presentation.PROP_HOVERED_ICON;
|
||||
import static com.intellij.openapi.actionSystem.Presentation.PROP_ICON;
|
||||
|
||||
public class PopupFactoryImpl extends JBPopupFactory {
|
||||
|
||||
/**
|
||||
@@ -232,6 +236,7 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
private final Runnable myDisposeCallback;
|
||||
private final Component myComponent;
|
||||
private final String myActionPlace;
|
||||
private IconHoverListener myIconsHoverListener;
|
||||
|
||||
public ActionGroupPopup(final String title,
|
||||
@NotNull ActionGroup actionGroup,
|
||||
@@ -337,6 +342,8 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
if (myDisposeCallback != null) {
|
||||
myDisposeCallback.run();
|
||||
}
|
||||
getList().removeMouseMotionListener(myIconsHoverListener);
|
||||
getList().removeListSelectionListener(myIconsHoverListener);
|
||||
ActionMenu.showDescriptionInStatusBar(true, myComponent, null);
|
||||
super.dispose();
|
||||
}
|
||||
@@ -381,6 +388,19 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
getList().repaint();
|
||||
}
|
||||
|
||||
public void installOnHoverIconsSupport(@NotNull IconListPopupRenderer iconListPopupRenderer) {
|
||||
//OnHover icons listener should be installed once
|
||||
assert myIconsHoverListener == null;
|
||||
myIconsHoverListener = new IconHoverListener(iconListPopupRenderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean beforeShow() {
|
||||
getList().addMouseMotionListener(myIconsHoverListener);
|
||||
getList().addListSelectionListener(myIconsHoverListener);
|
||||
return super.beforeShow();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static <T> T getActionByClass(@Nullable Object value, @NotNull ActionPopupStep actionPopupStep, @NotNull Class<T> actionClass) {
|
||||
ActionItem item = value instanceof ActionItem ? (ActionItem)value : null;
|
||||
@@ -388,6 +408,42 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
if (!actionPopupStep.isSelectable(item)) return null;
|
||||
return actionClass.isInstance(item.getAction()) ? actionClass.cast(item.getAction()) : null;
|
||||
}
|
||||
|
||||
private class IconHoverListener extends MouseMotionAdapter implements ListSelectionListener {
|
||||
@NotNull private IconListPopupRenderer myRenderer;
|
||||
|
||||
public IconHoverListener(@NotNull IconListPopupRenderer renderer) {
|
||||
myRenderer = renderer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mouseMoved(MouseEvent e) {
|
||||
Point point = e.getPoint();
|
||||
int index = getList().locationToIndex(point);
|
||||
Rectangle bounds = getList().getCellBounds(index, index);
|
||||
Object selectedValue = getList().getSelectedValue();
|
||||
if (selectedValue instanceof ActionItem) {
|
||||
((ActionItem)selectedValue).setIconHovered(myRenderer.iconContains(point));
|
||||
}
|
||||
if (bounds != null) {
|
||||
getList().repaint(bounds);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
if (!e.getValueIsAdjusting()) {
|
||||
int selected = getSelectedIndex();
|
||||
int unselected = e.getFirstIndex() == selected ? e.getLastIndex() : e.getFirstIndex();
|
||||
Object elementAt = getList().getModel().getElementAt(unselected);
|
||||
if (elementAt instanceof ActionItem) {
|
||||
ActionItem actionItem = (ActionItem)elementAt;
|
||||
actionItem.setIconHovered(false);
|
||||
getList().repaint();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -697,7 +753,7 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
private final AnAction myAction;
|
||||
private final String myText;
|
||||
private final boolean myIsEnabled;
|
||||
private final Icon myIcon;
|
||||
@Nullable private ActionStepBuilder.IconWrapper myIcon;
|
||||
private final boolean myPrependWithSeparator;
|
||||
private final String mySeparatorText;
|
||||
private final String myDescription;
|
||||
@@ -706,7 +762,7 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
@NotNull String text,
|
||||
@Nullable String description,
|
||||
boolean enabled,
|
||||
Icon icon,
|
||||
@Nullable ActionStepBuilder.IconWrapper icon,
|
||||
final boolean prependWithSeparator,
|
||||
String separatorText) {
|
||||
myAction = action;
|
||||
@@ -716,6 +772,21 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
myPrependWithSeparator = prependWithSeparator;
|
||||
mySeparatorText = separatorText;
|
||||
myDescription = description;
|
||||
myAction.getTemplatePresentation().addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
if (evt.getPropertyName() == PROP_ICON || evt.getPropertyName() == PROP_HOVERED_ICON) {
|
||||
updateIcons();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void updateIcons() {
|
||||
// we can't set icons if it hasn't existed before, because alignment will be destroyed; use IconWrapper with null icon instead of null
|
||||
if (myIcon == null) return;
|
||||
Presentation presentation = myAction.getTemplatePresentation();
|
||||
myIcon.setIcons(presentation.getIcon(), presentation.getHoveredIcon());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -728,7 +799,8 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
return myText;
|
||||
}
|
||||
|
||||
public Icon getIcon() {
|
||||
@Nullable
|
||||
public ActionStepBuilder.IconWrapper getIcon() {
|
||||
return myIcon;
|
||||
}
|
||||
|
||||
@@ -756,6 +828,12 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
public String toString() {
|
||||
return myText;
|
||||
}
|
||||
|
||||
public void setIconHovered(boolean isHovered) {
|
||||
if (myIcon != null) {
|
||||
myIcon.setHovered(isHovered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ActionPopupStep implements ListPopupStepEx<ActionItem>, MnemonicNavigationFilter<ActionItem>, SpeedSearchFilter<ActionItem> {
|
||||
@@ -966,7 +1044,7 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
private boolean myPrependWithSeparator;
|
||||
private String mySeparatorText;
|
||||
private final boolean myHonorActionMnemonics;
|
||||
private Icon myEmptyIcon;
|
||||
private IconWrapper myEmptyIcon;
|
||||
private int myMaxIconWidth = -1;
|
||||
private int myMaxIconHeight = -1;
|
||||
@NotNull private String myActionPlace;
|
||||
@@ -998,7 +1076,7 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
|
||||
public void buildGroup(@NotNull ActionGroup actionGroup) {
|
||||
calcMaxIconSize(actionGroup);
|
||||
myEmptyIcon = myMaxIconHeight != -1 && myMaxIconWidth != -1 ? EmptyIcon.create(myMaxIconWidth, myMaxIconHeight) : null;
|
||||
myEmptyIcon = myMaxIconHeight != -1 && myMaxIconWidth != -1 ? createWrapper(null) : null;
|
||||
|
||||
appendActionsFromGroup(actionGroup);
|
||||
|
||||
@@ -1094,26 +1172,27 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
}
|
||||
|
||||
Icon icon = presentation.isEnabled() ? presentation.getIcon() : IconLoader.getDisabledIcon(presentation.getIcon());
|
||||
if (icon == null) {
|
||||
IconWrapper iconWrapper;
|
||||
if (icon == null && presentation.getHoveredIcon() == null) {
|
||||
@NonNls final String actionId = ActionManager.getInstance().getId(action);
|
||||
if (actionId != null && actionId.startsWith("QuickList.")) {
|
||||
icon = AllIcons.Actions.QuickList;
|
||||
iconWrapper = createWrapper(AllIcons.Actions.QuickList);
|
||||
}
|
||||
else if (action instanceof Toggleable) {
|
||||
boolean toggled = Boolean.TRUE.equals(presentation.getClientProperty(Toggleable.SELECTED_PROPERTY));
|
||||
icon = toggled? new IconWrapper(PlatformIcons.CHECK_ICON) : myEmptyIcon;
|
||||
iconWrapper = toggled ? createWrapper(PlatformIcons.CHECK_ICON) : myEmptyIcon;
|
||||
}
|
||||
else {
|
||||
icon = myEmptyIcon;
|
||||
iconWrapper = myEmptyIcon;
|
||||
}
|
||||
}
|
||||
else {
|
||||
icon = new IconWrapper(icon);
|
||||
iconWrapper = new IconWrapper(icon, presentation.getHoveredIcon(), myMaxIconWidth, myMaxIconHeight);
|
||||
}
|
||||
boolean prependSeparator = (!myListModel.isEmpty() || mySeparatorText != null) && myPrependWithSeparator;
|
||||
assert text != null : action + " has no presentation";
|
||||
myListModel.add(
|
||||
new ActionItem(action, text, (String)presentation.getClientProperty(JComponent.TOOL_TIP_TEXT_KEY), presentation.isEnabled(), icon,
|
||||
new ActionItem(action, text, (String)presentation.getClientProperty(JComponent.TOOL_TIP_TEXT_KEY), presentation.isEnabled(), iconWrapper,
|
||||
prependSeparator, mySeparatorText));
|
||||
myPrependWithSeparator = false;
|
||||
mySeparatorText = null;
|
||||
@@ -1123,28 +1202,40 @@ public class PopupFactoryImpl extends JBPopupFactory {
|
||||
/**
|
||||
* Adjusts icon size to maximum, so that icons with different sizes were aligned correctly.
|
||||
*/
|
||||
private class IconWrapper implements Icon {
|
||||
public static class IconWrapper extends IconUtil.IconSizeWrapper {
|
||||
|
||||
private Icon myIcon;
|
||||
@Nullable private Icon myIcon;
|
||||
@Nullable private Icon myHoverIcon;
|
||||
|
||||
IconWrapper(Icon icon) {
|
||||
myIcon = icon;
|
||||
private boolean isHovered;
|
||||
|
||||
public IconWrapper(@Nullable Icon icon, @Nullable Icon hoverIcon, int width, int height) {
|
||||
super(null, width, height);
|
||||
setIcons(icon, hoverIcon);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void paintIcon(Component c, Graphics g, int x, int y) {
|
||||
myIcon.paintIcon(c, g, x, y);
|
||||
paintIcon(myHoverIcon != null && isHovered ? myHoverIcon : myIcon, c, g, x, y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconWidth() {
|
||||
return myMaxIconWidth;
|
||||
public boolean isHovered() {
|
||||
return isHovered;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getIconHeight() {
|
||||
return myMaxIconHeight;
|
||||
public void setHovered(boolean hovered) {
|
||||
isHovered = hovered;
|
||||
}
|
||||
|
||||
public void setIcons(@Nullable Icon icon, @Nullable Icon hoveredIcon) {
|
||||
myIcon = icon;
|
||||
myHoverIcon = hoveredIcon;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public IconWrapper createWrapper(@Nullable Icon icon) {
|
||||
return new IconWrapper(icon, null, myMaxIconWidth, myMaxIconHeight);
|
||||
}
|
||||
|
||||
private Presentation getPresentation(@NotNull AnAction action) {
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.ui.popup.list;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
public interface IconListPopupRenderer {
|
||||
|
||||
boolean iconContains(@NotNull Point point);
|
||||
}
|
||||
Reference in New Issue
Block a user