balloons -- dialog style

This commit is contained in:
Kirill Kalishev
2011-07-14 13:28:57 +04:00
parent 9b9cf8c3fd
commit e09e0b60b9
12 changed files with 220 additions and 54 deletions
@@ -127,15 +127,7 @@ public abstract class AbstractInplaceVariableIntroducer<E extends PsiElement> ex
final JComponent component = getComponent();
if (component == null) return;
if (ApplicationManager.getApplication().isHeadlessEnvironment()) return;
final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createBalloonBuilder(component);
balloonBuilder.setFadeoutTime(0)
.setFillColor(UIManager.getColor("Panel.background"))
.setAnimationCycle(100)
.setHideOnClickOutside(false)
.setHideOnKeyOutside(false)
.setHideOnAction(false)
.setCloseButtonEnabled(true);
final BalloonBuilder balloonBuilder = JBPopupFactory.getInstance().createDialogBalloonBuilder(component, null);
final RelativePoint target = JBPopupFactory.getInstance().guessBestPopupLocation(myEditor);
final Point screenPoint = target.getScreenPoint();
myBalloon = balloonBuilder.createBalloon();
@@ -81,4 +81,10 @@ public interface BalloonBuilder {
@NotNull
BalloonBuilder setTitle(@Nullable String title);
@NotNull
BalloonBuilder setContentInsets(Insets insets);
@NotNull
BalloonBuilder setShadow(boolean shadow);
}
@@ -256,6 +256,7 @@ public abstract class JBPopupFactory {
public abstract BalloonBuilder createBalloonBuilder(@NotNull JComponent content);
public abstract BalloonBuilder createDialogBalloonBuilder(@NotNull JComponent content, String title);
public abstract BalloonBuilder createHtmlTextBalloonBuilder(@NotNull String htmlContent, @Nullable Icon icon, Color fillColor, @Nullable HyperlinkListener listener);
@@ -15,23 +15,19 @@
*/
package com.intellij.ui.components;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
public class JBLabel extends JLabel {
private Style myStyle = Style.REGULAR;
public enum Style {REGULAR, SMALL}
private UIUtil.Style myStyle = UIUtil.Style.REGULAR;
public JBLabel() {
}
public JBLabel(@NotNull Style style) {
public JBLabel(@NotNull UIUtil.Style style) {
setStyle(style);
}
@@ -43,7 +39,7 @@ public class JBLabel extends JLabel {
super(text);
}
public JBLabel(@NotNull String text, @NotNull Style style) {
public JBLabel(@NotNull String text, @NotNull UIUtil.Style style) {
super(text);
setStyle(style);
}
@@ -60,22 +56,12 @@ public class JBLabel extends JLabel {
super(text, icon, horizontalAlignment);
}
public void setStyle(@NotNull Style style) {
public void setStyle(@NotNull UIUtil.Style style) {
myStyle = style;
if (SystemInfo.isMac) {
putClientProperty("JComponent.sizeVariant", style == Style.REGULAR ? "regular" : "small");
}
else {
setFont(UIUtil.getFont(style == Style.SMALL ? UIUtil.FontSize.SMALL : UIUtil.FontSize.NORMAL, getFont()));
}
Container p = getParent();
if (p != null) {
SwingUtilities.updateComponentTreeUI(p);
}
UIUtil.applyStyle(style, this);
}
public Style getStyle() {
public UIUtil.Style getStyle() {
return myStyle;
}
}
+1
View File
@@ -29,6 +29,7 @@
<orderEntry type="library" name="jna" level="project" />
<orderEntry type="library" name="http-client-3.1" level="project" />
<orderEntry type="library" name="winp" level="project" />
<orderEntry type="library" name="swingx" level="project" />
</component>
</module>
@@ -16,6 +16,8 @@
package com.intellij.openapi.ui.impl;
import com.intellij.openapi.util.IconLoader;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.jdesktop.swingx.graphics.ShadowRenderer;
import javax.swing.*;
import java.awt.*;
@@ -104,4 +106,56 @@ public class ShadowBorderPainter {
public static BufferedImage createPopupShadow(final JComponent c, final int width, final int height) {
return createShadow(c, width, height, true);
}
public static Shadow createShadow(Image source, int x, int y, boolean paintSource, int shadowSize) {
int size = shadowSize;
final float w = source.getWidth(null);
final float h = source.getHeight(null);
float ratio = w /h;
float deltaX = size;
float deltaY = size / ratio;
final Image scaled = source.getScaledInstance((int)(w + deltaX), (int)(h + deltaY), Image.SCALE_SMOOTH);
final BufferedImage s =
GraphicsUtilities.createCompatibleTranslucentImage(scaled.getWidth(null), scaled.getHeight(null));
final Graphics2D graphics = (Graphics2D)s.getGraphics();
graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics.drawImage(scaled, 0, 0, null);
final BufferedImage shadow = new ShadowRenderer(size, .25f, Color.black).createShadow(s);
if (paintSource) {
final Graphics imgG = shadow.getGraphics();
final double d = size * 0.5;
imgG.drawImage(source, (int)(size + d), (int)(size + d / ratio), null);
}
return new Shadow(shadow, x - size - 5, y - size + 2);
}
public static class Shadow {
int x;
int y;
Image image;
public Shadow(Image image, int x, int y) {
this.x = x;
this.y = y;
this.image = image;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public Image getImage() {
return image;
}
}
}
@@ -25,6 +25,7 @@ import com.intellij.openapi.actionSystem.DataContext;
import com.intellij.openapi.actionSystem.ex.AnActionListener;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.MessageType;
import com.intellij.openapi.ui.impl.ShadowBorderPainter;
import com.intellij.openapi.ui.popup.Balloon;
import com.intellij.openapi.ui.popup.JBPopupListener;
import com.intellij.openapi.ui.popup.LightweightWindow;
@@ -77,7 +78,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
private final Color myBorderColor;
private final Color myFillColor;
private final Insets myContainerInsets = new Insets(2, 2, 2, 2);
private final Insets myContainerInsets;
private boolean myLastMoveWasInsideBalloon;
@@ -153,6 +154,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
private JLabel myTitleLabel;
private boolean myAnimationEnabled = true;
private boolean myShadow = false;
public boolean isInsideBalloon(MouseEvent me) {
return isInside(new RelativePoint(me));
@@ -206,7 +208,9 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
int positioChangeXShfit,
int positionChangeYShift,
boolean dialogMode,
String title) {
String title,
Insets contentInsets,
boolean shadow) {
myBorderColor = borderColor;
myFillColor = fillColor;
myContent = content;
@@ -246,8 +250,21 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
};
}
myShadow = shadow;
myContainerInsets = contentInsets;
myFadeoutTime = fadeoutTime;
myAnimationCycle = animationCycle;
if (myDialogMode) {
new AwtVisitor(myContent) {
@Override
public boolean visit(Component component) {
UIUtil.applyStyle(UIUtil.Style.SMALL, component);
return false;
}
};
}
}
public void show(final RelativePoint target, final Balloon.Position position) {
@@ -407,7 +424,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
myComp.validate();
Rectangle rec = myComp.getBounds();
Rectangle rec = myComp.getContentBounds();
if (myShowPointer && !myPosition.isOkToHavePointer(myTargetPoint, rec, getPointerLength(myPosition), getPointerWidth(myPosition), getArc(), getNormalInset())) {
myShowPointer = false;
@@ -492,6 +509,8 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
myComp.clear();
myComp.myAlpha = 0f;
final int borderSize = getShadowBorderSize();
myComp.setBorder(new EmptyBorder(borderSize, borderSize, borderSize, borderSize));
myLayeredPane.add(myComp, JLayeredPane.POPUP_LAYER);
myPosition.updateBounds(this);
@@ -511,6 +530,10 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
}
}
private int getShadowBorderSize() {
return myShadow && Registry.is("ide.balloon.shadowEnabled") ? Registry.intValue("ide.balloon.shadow.size") : 0;
}
public void show(JLayeredPane pane) {
show(pane, null);
}
@@ -583,23 +606,35 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
int getArc() {
return 3;
return myDialogMode ? 6 : 3;
}
int getPointerWidth(AbstractPosition position) {
return position.isTopBottomPointer() ? 14 : 11;
if (myDialogMode) {
return position.isTopBottomPointer() ? 24 : 17;
} else {
return position.isTopBottomPointer() ? 14 : 11;
}
}
public static int getNormalInset() {
return 3;
}
static int getPointerLength(AbstractPosition position) {
return position.isTopBottomPointer() ? 10 : 8;
int getPointerLength(AbstractPosition position) {
return getPointerLength(position, myDialogMode);
}
public static int getPointerLength(Position position) {
return getPointerLength((getAbstractPositionFor(position)));
static int getPointerLength(AbstractPosition position, boolean dialogMode) {
if (dialogMode) {
return position.isTopBottomPointer() ? 16 : 14;
} else {
return position.isTopBottomPointer() ? 10 : 8;
}
}
public static int getPointerLength(Position position, boolean dialogMode) {
return getPointerLength(getAbstractPositionFor(position), dialogMode);
}
@@ -683,13 +718,13 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
abstract int getChangeShift(AbstractPosition original, int xShift, int yShift);
public void updateBounds(final BalloonImpl balloon) {
balloon.myComp._setBounds(getUpdatedBounds(balloon.myLayeredPane.getSize(),
balloon.myForcedBounds,
balloon.myComp.getPreferredSize(),
balloon.myShowPointer,
balloon.myTargetPoint,
balloon.myContainerInsets,
balloon.myCalloutshift));
final Rectangle bounds =
getUpdatedBounds(balloon.myLayeredPane.getSize(), balloon.myForcedBounds, balloon.myComp.getPreferredSize(), balloon.myShowPointer,
balloon.myTargetPoint, balloon.myContainerInsets, balloon.myCalloutshift);
final Point point = getShiftedPoint(bounds.getLocation(), -balloon.getShadowBorderSize());
bounds.setLocation(point);
balloon.myComp._setBounds(bounds);
}
public Rectangle getUpdatedBounds(Dimension layeredPaneSize,
@@ -718,6 +753,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
final GraphicsConfig cfg = new GraphicsConfig(g);
cfg.setAntialiasing(true);
Shape shape;
if (balloon.myShowPointer) {
shape = getPointingShape(bounds, g, pointTarget, balloon);
@@ -733,7 +769,8 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
if (balloon.myTitleLabel != null) {
Rectangle titleBounds = balloon.myTitleLabel.getBounds();
Insets inset = getTitleInsets(balloon.getNormalInset() - 1, getPointerLength(this) + 50);
final int shadow = balloon.getShadowBorderSize();
Insets inset = getTitleInsets(balloon.getNormalInset() - 1 + shadow, balloon.getPointerLength(this) + 50 + shadow);
titleBounds.x -= inset.left + 1;
titleBounds.width += (inset.left + inset.right + 50);
@@ -891,7 +928,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
@Override
public Point getShiftedPoint(Point targetPoint, int shift) {
return new Point(targetPoint.x, targetPoint.y + shift);
return new Point(targetPoint.x, targetPoint.y - shift);
}
@Override
@@ -1120,6 +1157,7 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
private final BalloonImpl myBalloon;
private final JComponent myContent;
private ShadowBorderPainter.Shadow myShadow;
private MyComponent(JComponent content, BalloonImpl balloon, EmptyBorder shapeBorder) {
setOpaque(false);
@@ -1147,6 +1185,16 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
myCloseRec = new CloseButton();
}
public Rectangle getContentBounds() {
final Rectangle bounds = super.getBounds();
final int shadow = myBalloon.getShadowBorderSize();
bounds.x += shadow;
bounds.width -= shadow * 2;
bounds.y += shadow;
bounds.height -= shadow * 2;
return bounds;
}
public void clear() {
myImage = null;
myAlpha = -1;
@@ -1188,13 +1236,20 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
final Graphics2D g2d = (Graphics2D)g;
final Point pointTarget = SwingUtilities.convertPoint(myLayeredPane, myBalloon.myTargetPoint, this);
Point pointTarget = SwingUtilities.convertPoint(myLayeredPane, myBalloon.myTargetPoint, this);
Rectangle shapeBounds = myContent.getBounds();
final int shadowSize = myBalloon.getShadowBorderSize();
if (shadowSize > 0) {
if (myShadow == null) {
initComponentImage(pointTarget, shapeBounds);
myShadow = ShadowBorderPainter.createShadow(myImage, 0, 0, false, shadowSize / 2);
}
}
if (myImage == null && myAlpha != -1) {
myImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
myBalloon.myPosition.paintComponent(myBalloon, shapeBounds, (Graphics2D)myImage.getGraphics(), pointTarget, this);
initComponentImage(pointTarget, shapeBounds);
}
if (myImage != null && myAlpha != -1) {
@@ -1203,10 +1258,20 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
g2d.drawImage(myImage, 0, 0, null);
}
else {
if (myShadow != null) {
g.drawImage(myShadow.getImage(), myShadow.getX(), myShadow.getY(), null);
}
myBalloon.myPosition.paintComponent(myBalloon, shapeBounds, (Graphics2D)g, pointTarget, this);
}
}
private void initComponentImage(Point pointTarget, Rectangle shapeBounds) {
if (myImage != null) return;
myImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
myBalloon.myPosition.paintComponent(myBalloon, shapeBounds, (Graphics2D)myImage.getGraphics(), pointTarget, this);
}
@Override
public void removeNotify() {
@@ -1245,6 +1310,8 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
int iconHeight = myBalloon.myCloseButton.getIconHeight();
Rectangle r = new Rectangle(lpBounds.x + lpBounds.width - iconWidth + (int)(iconWidth * 0.3), lpBounds.y - (int)(iconHeight * 0.3), iconWidth, iconHeight);
r.y += getShadowBorderSize();
r.x -= getShadowBorderSize();
myCloseRec.setBounds(r);
}
@@ -1376,7 +1443,8 @@ public class BalloonImpl implements Disposable, Balloon, LightweightWindow, Posi
//pane.setBorder(new LineBorder(Color.blue));
balloon.set(new BalloonImpl(new JLabel("FUCK"), Color.black, MessageType.ERROR.getPopupBackground(), true, true, true, true, true, 0, true, null, false, 500, 5, 0, 0, false, "This is the title"));
balloon.set(new BalloonImpl(new JLabel("FUCK"), Color.black, MessageType.ERROR.getPopupBackground(), true, true, true, true, true, 0, true, null, false, 500, 5, 0, 0, false, "This is the title",
new Insets(2, 2, 2, 2), true));
balloon.get().setShowPointer(true);
if (e.isShiftDown()) {
@@ -228,7 +228,7 @@ public class LightweightHint extends UserDataHolderBase implements Hint {
Dimension size = myComponent.getPreferredSize();
Balloon.Position position = myHintHint.getPreferredPosition();
int shift = BalloonImpl.getPointerLength(position);
int shift = BalloonImpl.getPointerLength(position, false);
switch (position) {
case below:
actualPoint.y += shift;
@@ -260,7 +260,7 @@ public class LightweightHint extends UserDataHolderBase implements Hint {
Point target = desiredLocation.getPointOn(pane).getPoint();
Balloon.Position pos = hintHint.getPreferredPosition();
int pointer = BalloonImpl.getPointerLength(pos) + BalloonImpl.getNormalInset();
int pointer = BalloonImpl.getPointerLength(pos, false) + BalloonImpl.getNormalInset();
if (pos == Balloon.Position.above || pos == Balloon.Position.below) {
boolean heightFit = target.y - size.height - pointer > 0 || target.y + size.height + pointer < paneSize.height;
return heightFit && size.width + pointer < paneSize.width;
@@ -50,6 +50,8 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder {
private boolean myHideOnAction = true;
private boolean myDialogMode;
private String myTitle;
private Insets myContentInsets = new Insets(2, 2, 2, 2);
private boolean myShadow = false;
public BalloonPopupBuilderImpl(@NotNull final JComponent content) {
myContent = content;
@@ -146,7 +148,7 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder {
@NotNull
public Balloon createBalloon() {
return new BalloonImpl(myContent, myBorder, myFill, myHideOnMouseOutside, myHideOnKeyOutside, myHideOnAction, myShowCalllout, myCloseButtonEnabled, myFadeoutTime, myHideOnFrameResize, myClickHandler, myCloseOnClick, myAnimationCycle,
myCalloutShift, myPositionChangeXShift, myPositionChangeYShift, myDialogMode, myTitle);
myCalloutShift, myPositionChangeXShift, myPositionChangeYShift, myDialogMode, myTitle, myContentInsets, myShadow);
}
@NotNull
@@ -175,4 +177,18 @@ public class BalloonPopupBuilderImpl implements BalloonBuilder {
myTitle = title;
return this;
}
@NotNull
@Override
public BalloonBuilder setContentInsets(Insets insets) {
myContentInsets = insets;
return this;
}
@NotNull
@Override
public BalloonBuilder setShadow(boolean shadow) {
myShadow = shadow;
return this;
}
}
@@ -746,6 +746,27 @@ public class PopupFactoryImpl extends JBPopupFactory {
return new BalloonPopupBuilderImpl(content);
}
@Override
public BalloonBuilder createDialogBalloonBuilder(@NotNull JComponent content, String title) {
final BalloonPopupBuilderImpl builder = new BalloonPopupBuilderImpl(content);
final Color bg = UIManager.getColor("Panel.background");
final Color borderOriginal = Color.darkGray;
final Color border = new Color(borderOriginal.getRed(), borderOriginal.getGreen(), borderOriginal.getBlue(), 75);
builder
.setDialogMode(true)
.setTitle(title)
.setAnimationCycle(200)
.setFillColor(bg)
.setBorderColor(border)
.setHideOnClickOutside(false)
.setHideOnKeyOutside(false)
.setHideOnAction(false)
.setCloseButtonEnabled(true)
.setShadow(true);
return builder;
}
public BalloonBuilder createHtmlTextBalloonBuilder(@NotNull final String htmlContent, @Nullable final Icon icon, final Color fillColor,
@Nullable final HyperlinkListener listener) {
@@ -79,6 +79,9 @@ ide.tooltip.reshowDelay=500
ide.tooltip.dismissDelay=4000
ide.tooltip.initialReshowDelay=300
ide.tooltip.autoDismissDeadZone=300
ide.balloon.shadowEnabled=true
ide.balloon.shadow.size=15
editor.balloonHints=true
editor.mouseSelectionStateResetTimeout=1000
@@ -68,6 +68,23 @@ public class UIUtil {
private static final String TABLE_DECORATION_KEY = "TABLE_DECORATION_KEY";
private static final Color DECORATED_ROW_BG_COLOR = new Color(242, 245, 249);
public static void applyStyle(@NotNull Style style, @NotNull Component comp) {
if (!(comp instanceof JComponent)) return;
JComponent c = (JComponent)comp;
if (SystemInfo.isMac) {
c.putClientProperty("JComponent.sizeVariant", style == Style.REGULAR ? "regular" : "small");
}
else {
c.setFont(getFont(style == Style.SMALL ? FontSize.SMALL : FontSize.NORMAL, c.getFont()));
}
Container p = c.getParent();
if (p != null) {
SwingUtilities.updateComponentTreeUI(p);
}
}
public enum FontSize { NORMAL, SMALL }
public static final char MNEMONIC = 0x1B;
@@ -2369,5 +2386,6 @@ public class UIUtil {
&& property instanceof Boolean && ((Boolean)property).booleanValue();
}
public enum Style {REGULAR, SMALL}
}