From a5cbc1988bcbded3efbc2d0ed4c28f1c858b3255 Mon Sep 17 00:00:00 2001 From: Alexey Pegov Date: Sun, 8 Aug 2010 15:37:38 +0400 Subject: [PATCH] popups (context menus, combobox popups) fix under aqua laf: round corners, selection, etc --- .../com/intellij/ide/ui/LafManagerImpl.java | 47 +++++++- .../com/intellij/ui/mac/MacPopupMenuUI.java | 112 ++++++++++++++++++ .../intellij/ui/plaf/beg/BegMenuItemUI.java | 40 ++++--- .../com/intellij/ui/plaf/beg/IdeaMenuUI.java | 23 +++- 4 files changed, 199 insertions(+), 23 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ui/mac/MacPopupMenuUI.java diff --git a/platform/platform-impl/src/com/intellij/ide/ui/LafManagerImpl.java b/platform/platform-impl/src/com/intellij/ide/ui/LafManagerImpl.java index beada842c6d9..94b92c4e39fb 100644 --- a/platform/platform-impl/src/com/intellij/ide/ui/LafManagerImpl.java +++ b/platform/platform-impl/src/com/intellij/ide/ui/LafManagerImpl.java @@ -27,11 +27,14 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.ui.ColoredSideBorder; import com.intellij.ui.IdeaBlueMetalTheme; import com.intellij.ui.ScreenUtil; +import com.intellij.ui.mac.MacPopupMenuUI; import com.intellij.ui.plaf.beg.*; +import com.intellij.util.ui.UIUtil; import com.sun.java.swing.plaf.windows.WindowsLookAndFeel; import org.jdom.Element; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.border.Border; @@ -43,6 +46,8 @@ import javax.swing.plaf.metal.MetalLookAndFeel; import javax.swing.text.DefaultEditorKit; import java.awt.*; import java.awt.event.KeyEvent; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.Arrays; import java.util.Comparator; import java.util.HashMap; @@ -307,6 +312,7 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo PopupFactory popupFactory; + final PopupFactory oldFactory = PopupFactory.getSharedInstance(); if(HEAVY_WEIGHT_POPUP.equals(popupWeight)){ popupFactory=new PopupFactory(){ public Popup getPopup( @@ -316,7 +322,7 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo int y ) throws IllegalArgumentException{ final Point point = fixPopupLocation(contents, x, y); - return new Popup(owner,contents,point.x,point.y){}; + return oldFactory.getPopup(owner, contents, point.x, point.y); } }; }else if(MEDIUM_WEIGHT_POPUP.equals(popupWeight)){ @@ -328,14 +334,49 @@ public final class LafManagerImpl extends LafManager implements ApplicationCompo private Popup createPopup(final Component owner, final Component contents, final int x, final int y) { final Point point = fixPopupLocation(contents, x, y); - final Popup popup = super.getPopup(owner, contents, point.x, point.y); - return popup; + return oldFactory.getPopup(owner, contents, point.x, point.y); } }; }else{ throw new IllegalStateException("unknown value of property -Didea.popup.weight: "+popupWeight); } PopupFactory.setSharedInstance(popupFactory); + + // update ui for popup menu to get round corners + if (UIUtil.isUnderAquaLookAndFeel()) { + final UIDefaults uiDefaults = UIManager.getLookAndFeelDefaults(); + uiDefaults.put("PopupMenuUI", MacPopupMenuUI.class.getCanonicalName()); + final Icon icon = getAquaMenuInvertedIcon(); + if (icon != null) { + uiDefaults.put("Menu.invertedArrowIcon", icon); + } + } + } + + @Nullable + private static Icon getAquaMenuInvertedIcon() { + if (!UIUtil.isUnderAquaLookAndFeel()) return null; + final Icon arrow = (Icon) UIManager.get("Menu.arrowIcon"); + if (arrow == null) return null; + + try { + final Method method = arrow.getClass().getMethod("getInvertedIcon"); + if (method != null) { + method.setAccessible(true); + return (Icon) method.invoke(arrow); + } + + return null; + } + catch (NoSuchMethodException e1) { + return null; + } + catch (InvocationTargetException e1) { + return null; + } + catch (IllegalAccessException e1) { + return null; + } } private Point fixPopupLocation(final Component contents, final int x, final int y) { diff --git a/platform/platform-impl/src/com/intellij/ui/mac/MacPopupMenuUI.java b/platform/platform-impl/src/com/intellij/ui/mac/MacPopupMenuUI.java new file mode 100644 index 000000000000..079947cb7fe8 --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/mac/MacPopupMenuUI.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2010 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.mac; + +import javax.swing.*; +import javax.swing.plaf.ComponentUI; +import javax.swing.plaf.basic.BasicPopupMenuUI; +import java.awt.*; +import java.awt.event.MouseEvent; + +/** + * User: spLeaner + */ +public class MacPopupMenuUI extends BasicPopupMenuUI { + static Stroke THREE_PIXEL_STROKE = new BasicStroke(3F); + + public MacPopupMenuUI() { + } + + public static ComponentUI createUI(final JComponent c) { + return new MacPopupMenuUI(); + } + + public boolean isPopupTrigger(final MouseEvent event) { + return event.isPopupTrigger(); + } + + @Override + public void installUI(JComponent c) { + super.installUI(c); + } + + public void paint(final Graphics g, final JComponent jcomponent) { + if (!(g instanceof Graphics2D)) { + super.paint(g, jcomponent); + return; + } + + Graphics2D graphics2d = (Graphics2D)g.create(); + Rectangle rectangle = popupMenu.getBounds(); + paintRoundRect(graphics2d, rectangle); + clipEdges(graphics2d, rectangle); + graphics2d.dispose(); + + super.paint(g, jcomponent); + } + + private static void paintRoundRect(Graphics2D graphics2d, Rectangle rectangle) { + graphics2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graphics2d.setComposite(AlphaComposite.Clear); + graphics2d.setStroke(THREE_PIXEL_STROKE); + graphics2d.drawRoundRect(-2, -2, rectangle.width + 3, rectangle.height + 3, 12, 12); + } + + protected void clipEdges(Graphics2D graphics2d, Rectangle rectangle) { + Component component = popupMenu.getInvoker(); + if (!(component instanceof JMenu)) return; + + Rectangle rectangle1 = component.getBounds(); + + rectangle1.setLocation(component.getLocationOnScreen()); + rectangle.setLocation(popupMenu.getLocationOnScreen()); + + Point point = new Point((int)rectangle1.getCenterX(), (int)rectangle1.getCenterY()); + if (rectangle.contains(point)) return; + + graphics2d.setComposite(AlphaComposite.SrcOver); + graphics2d.setColor(popupMenu.getBackground()); + + Point point1 = new Point((int)rectangle.getCenterX(), (int)rectangle.getCenterY()); + boolean flag = point.y <= point1.y; + + if (rectangle1.x + rectangle1.width < rectangle.x + 10) { + if (flag) { + graphics2d.fillRect(-2, -2, 8, 8); + return; + } + else { + graphics2d.fillRect(-2, rectangle.height - 6, 8, 8); + return; + } + } + + if (rectangle.x + rectangle.width < rectangle1.x + 10) { + if (flag) { + graphics2d.fillRect(rectangle.width - 6, -2, 8, 8); + return; + } + else { + graphics2d.fillRect(rectangle.width - 6, rectangle.height - 6, 8, 8); + return; + } + } + + if (rectangle1.y + rectangle1.height < rectangle.y + 10) { + graphics2d.fillRect(-2, -2, rectangle.width + 4, 8); + } + } +} diff --git a/platform/platform-impl/src/com/intellij/ui/plaf/beg/BegMenuItemUI.java b/platform/platform-impl/src/com/intellij/ui/plaf/beg/BegMenuItemUI.java index 294d9fcbd7dc..b6b12c27c346 100644 --- a/platform/platform-impl/src/com/intellij/ui/plaf/beg/BegMenuItemUI.java +++ b/platform/platform-impl/src/com/intellij/ui/plaf/beg/BegMenuItemUI.java @@ -23,6 +23,7 @@ import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.event.MenuDragMouseEvent; import javax.swing.event.MenuDragMouseListener; import javax.swing.event.MouseInputListener; @@ -57,6 +58,8 @@ public class BegMenuItemUI extends BasicMenuItemUI { @NonNls public static final String AQUA_LOOK_AND_FEEL_CLASS_NAME = "apple.laf.AquaLookAndFeel"; @NonNls public static final String GET_KEY_MODIFIERS_TEXT = "getKeyModifiersText"; + private static final Border SELECTED_BACKGROUND_PAINTER = (Border) UIManager.get("MenuItem.selectedBackgroundPainter"); + /** invoked by reflection */ public static ComponentUI createUI(JComponent component) { return new BegMenuItemUI(); @@ -108,18 +111,22 @@ public class BegMenuItemUI extends BasicMenuItemUI { g.setColor(jmenuitem.getBackground()); g.fillRect(0, 0, j1, k1); if (buttonmodel.isArmed() || (comp instanceof JMenu) && buttonmodel.isSelected()){ - g.setColor(selectionBackground); - if (icon2 != null){ - g.fillRect(k, 0, j1 - k, k1); - } - else{ - g.fillRect(0, 0, j1, k1); - //graphics.setColor(BegResources.q); - //graphics.drawLine(0, 0, 0, k1); + if (UIUtil.isUnderAquaLookAndFeel()) { + SELECTED_BACKGROUND_PAINTER.paintBorder(comp, g, 0, 0, j1, k1); + } else { g.setColor(selectionBackground); + if (icon2 != null){ + g.fillRect(k, 0, j1 - k, k1); + } + else{ + g.fillRect(0, 0, j1, k1); + //graphics.setColor(BegResources.q); + //graphics.drawLine(0, 0, 0, k1); + g.setColor(selectionBackground); + } + //graphics.setColor(BegResources.r); + //graphics.drawLine(j1 - 1, 0, j1 - 1, k1); } - //graphics.setColor(BegResources.r); - //graphics.drawLine(j1 - 1, 0, j1 - 1, k1); } g.setColor(color2); } @@ -180,11 +187,16 @@ public class BegMenuItemUI extends BasicMenuItemUI { if (keyStrokeText != null && !keyStrokeText.equals("")){ g.setFont(acceleratorFont); if (buttonmodel.isEnabled()){ - if (buttonmodel.isArmed() || (comp instanceof JMenu) && buttonmodel.isSelected()){ - g.setColor(acceleratorSelectionForeground); + if (UIUtil.isUnderAquaLookAndFeel() && (buttonmodel.isArmed() || (comp instanceof JMenu) && buttonmodel.isSelected())) { + g.setColor(selectionForeground); } - else{ - g.setColor(acceleratorForeground); + else { + if (buttonmodel.isArmed() || (comp instanceof JMenu) && buttonmodel.isSelected()) { + g.setColor(acceleratorSelectionForeground); + } + else { + g.setColor(acceleratorForeground); + } } BasicGraphicsUtils.drawString(g, keyStrokeText, 0, c.x, c.y + fontmetrics.getAscent()); } diff --git a/platform/platform-impl/src/com/intellij/ui/plaf/beg/IdeaMenuUI.java b/platform/platform-impl/src/com/intellij/ui/plaf/beg/IdeaMenuUI.java index ea96dc51a957..dd1f40bedbf1 100644 --- a/platform/platform-impl/src/com/intellij/ui/plaf/beg/IdeaMenuUI.java +++ b/platform/platform-impl/src/com/intellij/ui/plaf/beg/IdeaMenuUI.java @@ -19,6 +19,7 @@ import com.intellij.Patches; import com.intellij.util.ui.UIUtil; import javax.swing.*; +import javax.swing.border.Border; import javax.swing.event.MenuKeyEvent; import javax.swing.event.MenuKeyListener; import javax.swing.plaf.ComponentUI; @@ -43,6 +44,9 @@ public class IdeaMenuUI extends BasicMenuUI{ private static final Rectangle ourIconRect = new Rectangle(); private static final Rectangle ourViewRect = new Rectangle(32767, 32767); + private static final Border SELECTED_BACKGROUND_PAINTER = (Border) UIManager.get("MenuItem.selectedBackgroundPainter"); + private static final Icon INVERTED_ARROW_ICON = (Icon) UIManager.get("Menu.invertedArrowIcon"); + /** invoked by reflection */ public static ComponentUI createUI(JComponent component) { return new IdeaMenuUI(); @@ -109,12 +113,17 @@ public class IdeaMenuUI extends BasicMenuUI{ g.setColor(jMenu.getBackground()); g.fillRect(0, 0, jMenu.getWidth(), jMenu.getHeight()); if (buttonmodel.isArmed() || buttonmodel.isSelected()){ - g.setColor(selectionBackground); - if (allowedIcon != null){ - g.fillRect(k, 0, jMenu.getWidth() - k, jMenu.getHeight()); - }else{ - g.fillRect(0, 0, jMenu.getWidth(), jMenu.getHeight()); + if (UIUtil.isUnderAquaLookAndFeel()) { + SELECTED_BACKGROUND_PAINTER.paintBorder(comp, g, 0, 0, jMenu.getWidth(), jMenu.getHeight()); + } else { g.setColor(selectionBackground); + if (allowedIcon != null) { + g.fillRect(k, 0, jMenu.getWidth() - k, jMenu.getHeight()); + } + else { + g.fillRect(0, 0, jMenu.getWidth(), jMenu.getHeight()); + g.setColor(selectionBackground); + } } } g.setColor(color2); @@ -179,7 +188,9 @@ public class IdeaMenuUI extends BasicMenuUI{ } if (useCheckAndArrow()){ try { - arrowIcon.paintIcon(comp, g, ourArrowIconRect.x, ourArrowIconRect.y); + if (UIUtil.isUnderAquaLookAndFeel() && buttonmodel.isSelected() && INVERTED_ARROW_ICON != null) { + INVERTED_ARROW_ICON.paintIcon(comp, g, ourArrowIconRect.x, ourArrowIconRect.y); + } else arrowIcon.paintIcon(comp, g, ourArrowIconRect.x, ourArrowIconRect.y); } catch (NullPointerException npe) { // GTKIconFactory$MenuArrowIcon.paintIcon since it doesn't expect to be given a null instead of SynthContext