Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

smooth rounded corners in swing

I want to make smooth rounded corners for my swing application, but I can't get my desired result...

here's the screenshots:

1.setShape() for JFrame :

enter image description here

2.overriding paintComponent() method for JPanel instead of using setShape() :

enter image description here

3.setBackground(new Color(0, 0, 0, 0)) for JFrame :

enter image description here

well, but there's a problem with text quality:

before step 3 :

enter image description here

after step 3 :

enter image description here

guys I'm confused, I've searched many times but nothing helped me... what should I do? please help me

here's the full code :

public class WelcomePage extends JFrame {

private Point initialClick;
private boolean end = false;
private JLabel jLabelAppTitle;
private JPanel jPanelExit;
private JLabel jLabelHint;
private int r = 220, g = 0, b = 0;
private int r2 = 10, g2 = 10, b2 = 10;
private boolean flag = false;

public WelcomePage() {
    initComponents();
//    setShape(new RoundRectangle2D.Double(0, 0, getWidth(), getHeight(), 15, 15));
    centerLocation();
    refreshPage();
}

public static void main(String args[]) {
    try {
        for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e) {
        e.printStackTrace();
    }
    EventQueue.invokeLater(() -> FadeTransitions.fadeIn(new WelcomePage(), FadeTransitions.NORMAL_FADE, true));
}

private void refreshPage() {
    Timer timer = new Timer(20, e -> {
        if (!end) {
            if (r == 220 && b == 0 && g < 220) {
                g++;
            } else if (g == 220 && b == 0 && r > 0) {
                r--;
            } else if (g == 220 && r == 0 && b < 220) {
                b++;
            } else if (b == 220 && r == 0 && g > 0) {
                g--;
            } else if (b == 220 && g == 0 && r < 220) {
                r++;
            } else if (r == 220 && g == 0 && b > 0) {
                b--;
            }
            if (!flag) {
                r2 += 5;
                g2 += 5;
                b2 += 5;
                if (r2 == 250) {
                    flag = true;
                }
            } else {
                r2 -= 5;
                g2 -= 5;
                b2 -= 5;
                if (r2 == 10) {
                    flag = false;
                }
            }
            jLabelAppTitle.setForeground(new Color(r, g, b));
            jLabelHint.setForeground(new Color(r2, g2, b2));
        } else {
            ((Timer) e.getSource()).stop();
        }
    });
    timer.setCoalesce(true);
    timer.setRepeats(true);
    timer.start();
}

private void centerLocation() throws HeadlessException {
    final Toolkit toolkit = Toolkit.getDefaultToolkit();
    final Dimension screenSize = toolkit.getScreenSize();
    final int x = (screenSize.width - this.getWidth()) / 2;
    final int y = (screenSize.height - this.getHeight()) / 2;
    this.setLocation(x, y);
}

@SuppressWarnings("unchecked")
private void initComponents() {

    JPanel jPanelMain = new JPanel() {
        @Override
        protected void paintComponent(Graphics g) {
            Graphics2D g2 = (Graphics2D) g.create();
            RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
            qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
            g2.setRenderingHints(qualityHints);
            g2.setPaint(Color.WHITE);
            g2.fillRoundRect(0, 0, getWidth(), getHeight(), 25, 25);
            g2.dispose();
        }
    };
    jPanelExit = new JPanel();
    JLabel jLabelExit = new JLabel();
    JLabel jLabelWelcome = new JLabel();
    jLabelAppTitle = new JLabel();
    jLabelHint = new JLabel();
    JButton jButtonGo = new JButton();
    setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
    setTitle("welcome to My App!");
    setUndecorated(true);
    setBackground(new Color(0, 0, 0, 0));

    addWindowListener(new WindowAdapter() {
        @Override
        public void windowClosing(WindowEvent e) {
            close();
        }
    });
    addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
        public void mouseDragged(MouseEvent evt) {
            thisMouseDragged(evt);
        }
    });
    addMouseListener(new java.awt.event.MouseAdapter() {
        public void mousePressed(MouseEvent evt) {
            thisMousePressed(evt);
        }
    });

    jPanelMain.setBackground(Color.WHITE);
    jPanelExit.setBackground(new Color(160, 0, 20));

    jLabelExit.setFont(new Font("Tahoma", Font.BOLD, 13));
    jLabelExit.setForeground(new Color(255, 255, 255));
    jLabelExit.setHorizontalAlignment(SwingConstants.CENTER);
    jLabelExit.setText("X");
    jLabelExit.setCursor(new Cursor(Cursor.HAND_CURSOR));
    jLabelExit.addMouseListener(new java.awt.event.MouseAdapter() {
        public void mouseClicked(MouseEvent evt) {
            jLabelExitMouseClicked();
        }

        public void mouseEntered(MouseEvent evt) {
            jLabelExitMouseEntered();
        }

        public void mouseExited(MouseEvent evt) {
            jLabelExitMouseExited();
        }
    });

    GroupLayout jPanelExitLayout = new GroupLayout(jPanelExit);
    jPanelExit.setLayout(jPanelExitLayout);
    jPanelExitLayout.setHorizontalGroup(
            jPanelExitLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addGroup(GroupLayout.Alignment.TRAILING, jPanelExitLayout.createSequentialGroup()
                            .addGap(0, 0, Short.MAX_VALUE)
                            .addComponent(jLabelExit, GroupLayout.PREFERRED_SIZE, 45, GroupLayout.PREFERRED_SIZE))
    );
    jPanelExitLayout.setVerticalGroup(
            jPanelExitLayout.createParallelGroup(GroupLayout.Alignment.LEADING)
                    .addGroup(GroupLayout.Alignment.TRAILING, jPanelExitLayout.createSequentialGroup()
                            .addGap(0, 0, Short.MAX_VALUE)
                            .addComponent(jLabelExit, GroupLayout.PREFERRED_SIZE, 22, GroupLayout.PREFERRED_SIZE))
    );

    jLabelWelcome.setFont(new Font("Tahoma", 0, 25));
    jLabelWelcome.setForeground(new Color(0, 0, 100));
    jLabelWelcome.setHorizontalAlignment(SwingConstants.CENTER);
    jLabelWelcome.setText("Welcome");

    jLabelAppTitle.setFont(new Font("MV Boli", 0, 29));
    jLabelAppTitle.setHorizontalAlignment(SwingConstants.CENTER);
    jLabelAppTitle.setText("My Swing App");

    jButtonGo.setBackground(new Color(100, 20, 80));
    jButtonGo.setFont(new Font("Tahoma", 0, 15));
    jButtonGo.setForeground(new Color(255, 255, 255));
    jButtonGo.setText("GO");
    jButtonGo.addActionListener(evt -> jButtonGoActionPerformed());

    jLabelHint.setFont(new Font("Tahoma", 0, 11));
    jLabelHint.setHorizontalAlignment(SwingConstants.CENTER);
    jLabelHint.setText("press GO button");

    javax.swing.GroupLayout jPanelMainLayout = new javax.swing.GroupLayout(jPanelMain);
    jPanelMain.setLayout(jPanelMainLayout);
    jPanelMainLayout.setHorizontalGroup(
            jPanelMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanelMainLayout.createSequentialGroup()
                            .addContainerGap(48, Short.MAX_VALUE)
                            .addGroup(jPanelMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanelMainLayout.createSequentialGroup()
                                            .addGap(0, 0, Short.MAX_VALUE)
                                            .addComponent(jPanelExit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                            .addGap(15, 15, 15))
                                    .addGroup(jPanelMainLayout.createSequentialGroup()
                                            .addGroup(jPanelMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
                                                    .addComponent(jLabelWelcome, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                    .addComponent(jLabelAppTitle, javax.swing.GroupLayout.DEFAULT_SIZE, 220, Short.MAX_VALUE)
                                                    .addComponent(jLabelHint, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                                                    .addComponent(jButtonGo, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                                            .addContainerGap(48, Short.MAX_VALUE))))
    );
    jPanelMainLayout.setVerticalGroup(
            jPanelMainLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(jPanelMainLayout.createSequentialGroup()
                            .addComponent(jPanelExit, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGap(64, 64, 64)
                            .addComponent(jLabelWelcome, javax.swing.GroupLayout.PREFERRED_SIZE, 28, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGap(98, 98, 98)
                            .addComponent(jLabelAppTitle)
                            .addGap(86, 86, 86)
                            .addComponent(jLabelHint)
                            .addGap(24, 24, 24)
                            .addComponent(jButtonGo, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addGap(86, 86, 86))
    );

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                            .addGap(0, 0, 0)
                            .addComponent(jPanelMain, javax.swing.GroupLayout.DEFAULT_SIZE, 316, Short.MAX_VALUE)
                            .addGap(0, 0, 0))
    );
    layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(layout.createSequentialGroup()
                            .addGap(0, 0, 0)
                            .addComponent(jPanelMain, javax.swing.GroupLayout.DEFAULT_SIZE, 473, Short.MAX_VALUE)
                            .addGap(0, 0, 0))
    );

    pack();
}

private void thisMousePressed(MouseEvent evt) {
    initialClick = evt.getPoint();
}

private void thisMouseDragged(MouseEvent evt) {
    int thisX = this.getLocation().x;
    int thisY = this.getLocation().y;
    int xMoved = (thisX + evt.getX()) - (thisX + initialClick.x);
    int yMoved = (thisY + evt.getY()) - (thisY + initialClick.y);
    int x = thisX + xMoved;
    int y = thisY + yMoved;
    this.setLocation(x, y);
}

private void jLabelExitMouseClicked() {
    close();
}

private void close() {
    end = true;
    FadeTransitions.fadeOut(this, FadeTransitions.FAST_FADE, FadeTransitions.EXIT_ON_CLOSE);
}

private void jLabelExitMouseEntered() {
    jPanelExit.setBackground(new Color(200, 0, 20));
}

private void jLabelExitMouseExited() {
    jPanelExit.setBackground(new Color(160, 0, 20));
}

private void jButtonGoActionPerformed() {
    end = true;
    FadeTransitions.run(this, new ServerManager(this), FadeTransitions.NORMAL_FADE, FadeTransitions.DISPOSE_ON_CLOSE);
}

}

thanks.

like image 286
FaNaJ Avatar asked Jul 30 '14 07:07

FaNaJ


2 Answers

Sorry, not an answer, but hopefully at least one step towards an acceptable answer: From my analysis so far, it might be that this is simply a bug somewhere deep (deeeep!) inside the rendering pipeline.

The following MVCE shows two (undecorated) frames, each containing a button. They are equal, except for the background of the frames. For one frame, the color is transparent, and for the other one, it is opaque.

import java.awt.Color;
import java.awt.Font;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class TextRenderBug extends JFrame {

    public static void main(String[] args)
    {
        setLookAndFeel();
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI(new Color(0,0,0  ), 400);
                createAndShowGUI(new Color(0,0,0,0), 600);
            }
        });
    }

    private static void setLookAndFeel()
    {
        try
        {
            for (UIManager.LookAndFeelInfo info : 
                    UIManager.getInstalledLookAndFeels())
            {
                if ("Nimbus".equals(info.getName()))
                {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        }
        catch (Exception e)
        {
            e.printStackTrace();
        }
    }

    private static void createAndShowGUI(Color background, int x)
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setUndecorated(true);
        f.setBackground(background);
        JButton b = new JButton("Text");
        b.setFont(new Font("Tahoma", 0, 15));
        f.getContentPane().add(b);
        f.setBounds(x, 400, 200, 50);
        f.setVisible(true);
    }
}

It clearly shows that the text is rendered differently, solely depending on the background being transparent - and of course, this should not be the case.

TextRenderBug

(This is not Nimbus-specific, by the way: It also applies to other LookAndFeels. Just remove the line where the LaF is set).

What I found out so far:

  • The behavior is somehow caused by the drawString method of the sun.swing.SwingUtilities2 class
  • It does not appear in all components. It can be observed on JButton and JLabel, but not on a normal JComponent
  • Update: It also does not depend on the font (although with other fonts, the effect is not so noticable). When rendered correctly, the font looks a little bit more bold, but of course, it is not simply the same font as a Font.BOLD.
  • The painting process is rather complicated.

(OK, some of you might already have known the latter).

Here is an example that shows the last observations, maybe it can serve as a starting point for further research of others:

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.lang.reflect.Method;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;

public class TextRenderBugTest
{
    public static void main(String[] args)
    {
        SwingUtilities.invokeLater(new Runnable()
        {
            @Override
            public void run()
            {
                createAndShowGUI(new Color(0,0,0  ), 400);
                createAndShowGUI(new Color(0,0,0,0), 600);
            }
        });
    }

    private static void createAndShowGUI(Color background, int x)
    {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setUndecorated(true);
        f.setBackground(background);

        JButton b = new JButton("Text");
        b.setFont(new Font("Tahoma", 0, 15));

        JComponent c = new ExampleComponent();
        c.setFont(new Font("Tahoma", 0, 15));

        f.getContentPane().setLayout(new GridLayout(0,1));
        f.getContentPane().add(b);
        f.getContentPane().add(c);

        f.setBounds(x, 400, 200, 100);
        f.setVisible(true);
    }

    static class ExampleComponent 
        //extends JComponent
        extends JButton
    {
        @Override
        protected void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
            g.setColor(getForeground());
            g.drawString("Text", 10, 20);
            drawStringWithSwingUtilities(g, 60, 20);
        }

        private void drawStringWithSwingUtilities(Graphics g, int x, int y)
        {
            try
            {
                Class<?> c = Class.forName("sun.swing.SwingUtilities2");
                Method m = c.getMethod("drawString", JComponent.class,
                    Graphics.class, String.class, int.class, int.class);
                m.invoke(null, this, g, "Text", x, y);
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }

        }

    }

}

I already tried to analyze this further, and played around with RenderingHints like KEY_TEXT_ANTIALIASING and KEY_TEXT_LCD_CONTRAST and other settings that are changed in the painting pipeline while it is heading towards the pixels that are finally placed on the screen, but no further insights until now.

(If you want to, you can add this information to your original question, then I'll delete this (not-)answer)

like image 171
Marco13 Avatar answered Sep 24 '22 00:09

Marco13


The only chance you have is working with regions. You will need to hardcode a bit and play with subtract, but eventually it will look fine. You can take a look at how it's done in mylyn's notification class: http://grepcode.com/file/repository.grepcode.com/java/eclipse.org/3.6/org.eclipse.mylyn.commons/ui/3.4.0/org/eclipse/mylyn/internal/provisional/commons/ui/AbstractNotificationPopup.java

Hope it helps, best of luck!

Edit: Just remembered this little tutorial which might help: http://www.java2s.com/Code/Java/SWT-JFace-Eclipse/SWTShellcreateanonrectangularwindow.htm

like image 25
spetrila Avatar answered Sep 25 '22 00:09

spetrila