From 6a2a7efa85141a0ac71b50474069df8989a9fd30 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Thu, 19 Aug 2010 16:02:48 +0400 Subject: [PATCH] light bulb fixed for single line editors: placed at the editor corner + autohide if editor is a part of combobox and it's popup is invoked --- .../impl/IntentionHintComponent.java | 143 ++++++++++++------ 1 file changed, 96 insertions(+), 47 deletions(-) diff --git a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java index 777b64573995..a82957fb20eb 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/intention/impl/IntentionHintComponent.java @@ -20,7 +20,6 @@ import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.ShowIntentionsPass; import com.intellij.codeInsight.hint.HintManagerImpl; -import com.intellij.codeInsight.hint.HintUtil; import com.intellij.codeInsight.hint.PriorityQuestionAction; import com.intellij.codeInsight.hint.ScrollAwareHint; import com.intellij.codeInsight.intention.IntentionAction; @@ -52,9 +51,9 @@ import org.jetbrains.annotations.TestOnly; import javax.swing.*; import javax.swing.border.Border; +import javax.swing.event.PopupMenuEvent; +import javax.swing.event.PopupMenuListener; import java.awt.*; -import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -76,29 +75,32 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll static final Icon ourQuickFixOffIcon = IconLoader.getIcon("/actions/quickfixOffBulb.png"); static final Icon ourArrowIcon = IconLoader.getIcon("/general/arrowDown.png"); static final Icon ourInactiveArrowIcon = new EmptyIcon(ourArrowIcon.getIconWidth(), ourArrowIcon.getIconHeight()); - private static final Border INACTIVE_BORDER = BorderFactory.createEmptyBorder(1, 1, 1, 1); - private static final Insets INACTIVE_MARGIN = new Insets(0, 0, 0, 0); - private static final Insets ACTIVE_MARGIN = new Insets(0, 0, 0, 0); + private static final Border INACTIVE_BORDER = BorderFactory.createEmptyBorder(6, 6, 6, 6); + private static final Border ACTIVE_BORDER = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLACK, 1), BorderFactory.createEmptyBorder(5, 5, 5, 5)); + + private static final Border INACTIVE_BORDER_SMALL = BorderFactory.createEmptyBorder(4, 4, 4, 4); + private static final Border ACTIVE_BORDER_SMALL = BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(Color.BLACK, 1), BorderFactory.createEmptyBorder(3, 3, 3, 3)); private final Editor myEditor; private static final Alarm myAlarm = new Alarm(); private final RowIcon myHighlightedIcon; - private final JButton myButton; + private final JLabel myIconLabel; private final Icon mySmartTagIcon; private final RowIcon myInactiveIcon; private static final int DELAY = 500; private final MyComponentHint myComponentHint; - private static final Color BACKGROUND_COLOR = new Color(255, 255, 255, 0); private boolean myPopupShown = false; private boolean myDisposed = false; private ListPopup myPopup; private final PsiFile myFile; private static final int LIGHTBULB_OFFSET = 20; + private PopupMenuListener myOuterComboboxPopupListener; + public static IntentionHintComponent showIntentionHint(Project project, final PsiFile file, Editor editor, ShowIntentionsPass.IntentionsInfo intentions, boolean showExpanded) { final Point position = getHintPosition(editor); @@ -136,6 +138,15 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll myDisposed = true; myComponentHint.hide(); super.hide(); + + if (myOuterComboboxPopupListener != null) { + final Container ancestor = SwingUtilities.getAncestorOfClass(JComboBox.class, myEditor.getContentComponent()); + if (ancestor != null) { + ((JComboBox)ancestor).removePopupMenuListener(myOuterComboboxPopupListener); + } + + myOuterComboboxPopupListener = null; + } } public void editorScrolled() { @@ -192,18 +203,40 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll int line = pos.line; final Point position = editor.logicalPositionToXY(new LogicalPosition(line, 0)); - final int yShift = (ourIntentionIcon.getIconHeight() - editor.getLineHeight() - 1) / 2 - 1; - final int xShift = ourIntentionIcon.getIconWidth(); - LOG.assertTrue(editor.getComponent().isDisplayable()); - Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); - Point realPoint = new Point(Math.max(0,visibleArea.x - xShift), position.y + yShift- LIGHTBULB_OFFSET); - Point location = SwingUtilities.convertPoint(editor.getContentComponent(), realPoint, editor.getComponent().getRootPane().getLayeredPane()); + JComponent convertComponent = editor.getContentComponent(); + + Point realPoint; + final boolean oneLineEditor = editor.isOneLineMode(); + if (oneLineEditor) { + // place bulb at the corner of the surrounding component + final JComponent contentComponent = editor.getContentComponent(); + Container ancestorOfClass = SwingUtilities.getAncestorOfClass(JComboBox.class, contentComponent); + + if (ancestorOfClass != null) { + convertComponent = (JComponent) ancestorOfClass; + } else { + ancestorOfClass = SwingUtilities.getAncestorOfClass(JTextField.class, contentComponent); + if (ancestorOfClass != null) { + convertComponent = (JComponent) ancestorOfClass; + } + } + + realPoint = new Point(- (ourIntentionIcon.getIconWidth() / 2) - 4, - (ourIntentionIcon.getIconHeight() / 2)); + } else { + final int yShift = (ourIntentionIcon.getIconHeight() - editor.getLineHeight() - 1) / 2 - 1; + final int xShift = ourIntentionIcon.getIconWidth(); + + Rectangle visibleArea = editor.getScrollingModel().getVisibleArea(); + realPoint = new Point(Math.max(0,visibleArea.x - xShift), position.y + yShift- LIGHTBULB_OFFSET); + } + + Point location = SwingUtilities.convertPoint(convertComponent, realPoint, editor.getComponent().getRootPane().getLayeredPane()); return new Point(location.x, location.y); } - private IntentionHintComponent(@NotNull Project project, @NotNull PsiFile file, @NotNull Editor editor, + private IntentionHintComponent(@NotNull Project project, @NotNull PsiFile file, @NotNull final Editor editor, @NotNull ShowIntentionsPass.IntentionsInfo intentions) { ApplicationManager.getApplication().assertReadAccessAllowed(); myFile = file; @@ -220,6 +253,7 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll break; } } + mySmartTagIcon = showFix ? ourQuickFixIcon : ourBulbIcon; myHighlightedIcon = new RowIcon(2); @@ -230,28 +264,29 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll myInactiveIcon.setIcon(mySmartTagIcon, 0); myInactiveIcon.setIcon(ourInactiveArrowIcon, 1); - myButton = new JButton(myInactiveIcon); - myButton.setFocusable(false); - myButton.setMargin(INACTIVE_MARGIN); - myButton.setBorderPainted(false); - myButton.setContentAreaFilled(false); + myIconLabel = new JLabel(myInactiveIcon); + myIconLabel.setOpaque(false); - add(myButton, BorderLayout.CENTER); - setBorder(INACTIVE_BORDER); + add(myIconLabel, BorderLayout.CENTER); - myButton.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent e) { - showPopup(); + setBorder(editor.isOneLineMode() ? INACTIVE_BORDER_SMALL : INACTIVE_BORDER); + + myIconLabel.addMouseListener(new MouseAdapter() { + @Override + public void mousePressed(MouseEvent e) { + if (!e.isPopupTrigger() && e.getButton() == MouseEvent.BUTTON1) { + showPopup(); + } } - }); - myButton.addMouseListener(new MouseAdapter() { + @Override public void mouseEntered(MouseEvent e) { - onMouseEnter(); + onMouseEnter(editor.isOneLineMode()); } + @Override public void mouseExited(MouseEvent e) { - onMouseExit(); + onMouseExit(editor.isOneLineMode()); } }); @@ -264,36 +299,25 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll Disposer.dispose(this); } - private void onMouseExit() { + private void onMouseExit(final boolean small) { Window ancestor = SwingUtilities.getWindowAncestor(myPopup.getContent()); if (ancestor == null) { - myButton.setBackground(BACKGROUND_COLOR); - myButton.setIcon(myInactiveIcon); - setBorder(INACTIVE_BORDER); - myButton.setMargin(INACTIVE_MARGIN); - updateComponentHintSize(); + myIconLabel.setIcon(myInactiveIcon); + setBorder(small ? INACTIVE_BORDER_SMALL : INACTIVE_BORDER); } } - private void onMouseEnter() { - myButton.setBackground(HintUtil.QUESTION_COLOR); - myButton.setIcon(myHighlightedIcon); - setBorder(BorderFactory.createLineBorder(Color.black)); - myButton.setMargin(ACTIVE_MARGIN); - updateComponentHintSize(); + private void onMouseEnter(final boolean small) { + myIconLabel.setIcon(myHighlightedIcon); + setBorder(small ? ACTIVE_BORDER_SMALL : ACTIVE_BORDER); String acceleratorsText = KeymapUtil.getFirstKeyboardShortcutText( ActionManager.getInstance().getAction(IdeActions.ACTION_SHOW_INTENTION_ACTIONS)); if (acceleratorsText.length() > 0) { - myButton.setToolTipText(CodeInsightBundle.message("lightbulb.tooltip", acceleratorsText)); + myIconLabel.setToolTipText(CodeInsightBundle.message("lightbulb.tooltip", acceleratorsText)); } } - private void updateComponentHintSize() { - Component component = myComponentHint.getComponent(); - component.setSize(getPreferredSize().width, getHeight()); - } - @TestOnly public LightweightHint getComponentHint() { return myComponentHint; @@ -329,6 +353,31 @@ public class IntentionHintComponent extends JPanel implements Disposable, Scroll myPopupShown = false; } }); + + if (myEditor.isOneLineMode()) { + // hide popup on combobox popup show + final Container ancestor = SwingUtilities.getAncestorOfClass(JComboBox.class, myEditor.getContentComponent()); + if (ancestor != null) { + final JComboBox comboBox = (JComboBox)ancestor; + myOuterComboboxPopupListener = new PopupMenuListener() { + @Override + public void popupMenuWillBecomeVisible(PopupMenuEvent e) { + hide(); + } + + @Override + public void popupMenuWillBecomeInvisible(PopupMenuEvent e) { + } + + @Override + public void popupMenuCanceled(PopupMenuEvent e) { + } + }; + + comboBox.addPopupMenuListener(myOuterComboboxPopupListener); + } + } + Disposer.register(this, myPopup); Disposer.register(myPopup, new Disposable() { public void dispose() {