0

Add a neat rounded border that I found here to my JPanel. Very happy with the end result except for one small issue... on a mouse enter/mouse exit event I resize the border to highlight BUT the JLabel in the center of the JPanel moves to accommodate the extra space required for the rounded border.. I know this because I don't see this undesired behaviour when I use a standard line border!

Any tips/tricks/ideas welcome.. basically I want a rounded corner and the ability to change it's thickness but want to fix the JLabel's position at the center of the JPanel and ensure it doesn't move. Quick example below of undesired behaviour, simply mouse enter the JPanel to observe the JLabel move:

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;

public class temp extends JPanel {

    private Border brdrMouseEnter;
    private Border brdrMouseExit;

    public temp(){

        brdrMouseExit = new RoundedBorder(Color.gray, 1, 12, 0);
        brdrMouseEnter = new RoundedBorder(Color.blue, 4, 12, 0);

        this.setLayout(new BorderLayout());
        this.setBorder(brdrMouseExit);
        this.add(new JLabel("test", JLabel.CENTER));
        this.addMouseListener(new MouseAdapter() {
            public void mouseEntered(MouseEvent e) {
                JPanel thisPanel = (JPanel)e.getSource();
                thisPanel.setBorder(brdrMouseEnter);
            }
            public void mouseExited(MouseEvent e) {
                JPanel thisPanel = (JPanel)e.getSource();
                thisPanel.setBorder(brdrMouseExit);
            }

        });
    }

    public class RoundedBorder extends AbstractBorder {

        private Color color;
        private int thickness = 4;
        private int radii = 8;
        private int pointerSize = 7;
        private Insets insets = null;
        private BasicStroke stroke = null;
        private int strokePad;
        private int pointerPad = 4;
        private boolean left = true;
        RenderingHints hints;

        RoundedBorder(Color color){
            new RoundedBorder(color, 4, 8, 7);
        }

        RoundedBorder(Color color, int thickness, int radii, int pointerSize) {
            this.thickness = thickness;
            this.radii = radii;
            this.pointerSize = pointerSize;
            this.color = color;

            stroke = new BasicStroke(thickness);
            strokePad = thickness / 2;

            hints = new RenderingHints(
                    RenderingHints.KEY_ANTIALIASING,
                    RenderingHints.VALUE_ANTIALIAS_ON);

            int pad = radii + strokePad;
            int topPad = pad + pointerSize + strokePad;
            insets = new Insets(topPad, pad, pad, pad);
        }

        RoundedBorder(Color color, int thickness, int radii, int pointerSize, boolean left) {
            this(color, thickness, radii, pointerSize);
            this.left = left;
        }

        @Override
        public Insets getBorderInsets(Component c) {
            return insets;
        }

        @Override
        public Insets getBorderInsets(Component c, Insets insets) {
            return getBorderInsets(c);
        }

        @Override
        public void paintBorder(
                Component c,
                Graphics g,
                int x, int y,
                int width, int height) {

            Graphics2D g2 = (Graphics2D) g;

            int topLineY = height - thickness - pointerSize;
            RoundRectangle2D.Double bubble = new RoundRectangle2D.Double(
                    0 + strokePad,
                    0 + pointerSize,
                    width - thickness,
                    topLineY,
                    radii,
                    radii);

            Polygon pointer = new Polygon();
//          if (left) {
                // left point
                pointer.addPoint(
                        width - 25/*(strokePad + radii + pointerPad)*/,
                        strokePad +pointerSize);
                // right point
                pointer.addPoint(
                        width - 15/*(strokePad + radii + pointerPad + pointerSize)*/,
                        strokePad +pointerSize);
                // bottom point
                pointer.addPoint(
                        width - (25-(10/2))/*(strokePad + radii + pointerPad + (pointerSize / 2)*/,
                        strokePad);
//          } 

            Area area = new Area(bubble);
            area.add(new Area(pointer));

            g2.setRenderingHints(hints);

            // Paint the BG color of the parent, everywhere outside the clip
            // of the text bubble.
            Component parent  = c.getParent();
            if (parent!=null) {
                Color bg = parent.getBackground();
                Rectangle rect = new Rectangle(0,0,width, height);
                Area borderRegion = new Area(rect);
                borderRegion.subtract(area);
                g2.setClip(borderRegion);
                g2.setColor(bg);
                g2.fillRect(0, 0, width, height);
                g2.setClip(null);
            }

            g2.setColor(color);
            g2.setStroke(stroke);
            g2.draw(area);
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                temp ex = new temp();
                //ex.setVisible(true);
                JFrame f = new JFrame();
                f.getContentPane().add(ex);
                f.setSize(640, 480);
                f.setVisible(true);
                f.addWindowListener(new WindowAdapter() {
                    @Override
                    public void windowClosing(WindowEvent e) {
                        System.exit(1);
                    }
                });
            }
        });
    }
}
Community
  • 1
  • 1
Strokes
  • 157
  • 7
  • 23

1 Answers1

1

Try to introduce compound border to fill the extra space.

E.g. exit border could be created like this.

brdrMouseExit = new CompoundBorder(new EmptyBorder(1,1,1,1),
                                   new RoundedBorder(Color.gray, 1, 12, 0));

You should ply with empty border insets to achieve full extra space filling.

StanislavL
  • 56,971
  • 9
  • 68
  • 98
  • Yes i've tried this already... still not getting desired effect. You see I want to change the border thickness on mouse enter.. this seems to be the source of the problem! – Strokes Apr 07 '15 at 14:28