Graphical User Interface - Buttons

I’m making a multiplayer RPG with a friend. I’ve basically finished writing the server skeleton (feel free to take a look and tell me what you think), but there isn’t much more I can do until we work on the client and get some stuff sent to the server to interpret.

What I need help with right now is the best way to handle a graphical user interface. I need help with the entire thing in general, like:

  • When do you paint it?
  • How can you have intractable features, like custom buttons.

A good example would be the minecraft ‘options’ menu, how they have custom input fields:

http://2.bp.blogspot.com/-NplBU8Y_sPI/TWQzDF1BmEI/AAAAAAAAADQ/ZjBRJRJr2Nc/s1600/Untitled3.png

Any help is appreciated.

EDIT - Ok I think I know how to do buttons, I can just create a custom component called CustomButton or something that has a backing image or something right? And use the mouse events to handle input?

I’m confused. Why wouldn’t you just use a JButton?

Recommended reading: http://download.oracle.com/javase/tutorial/uiswing/components/button.html

Are you using Java2D or LWJGL?

Java2D

Thanks, but I’m not that stupid. If I wanted a regular button I’d use one. You probably just didn’t see the bolded and underlined word “custom” in my list? I could probably extend JButton and override paint or whatever, but that’s what I edited into my post basically and I’m just asking people if that’s the proper way to do it.

Well drawing a button Java2D is simple, you just draw a rectangle, fill it with a color or a background, and draw some text. Then your mouse listener and mouse motion listener would handle hovering, pressing, and releasing.

Yea. After replying I did some more research and found a better way than what I thought.

Thanks though.

Woah, I never called you stupid. I just don’t understand why you wouldn’t use a JButton and override paintComponent() or even just set its display properties (border, color, etc). You could even display an image on it. I don’t understand why you wouldn’t do that- I’m not calling you stupid, I’m asking you a question. Why wouldn’t you use a JButton?

---- I feel stoopid ----

Your post is the first mention of Canvas on this page. So unless you noticed or interpreted something that wasn’t super obvious, it’s not unreasonable to think that normal containers and buttons would be put together to create a menu like the image he used in his first post, especially given that he needed “help with the entire thing”. You don’t usually want to start from scratch in a situation like that.

You’re right, I got mixed up with another thread. Well if the OP is drawing on a Swing container like a JPanel, then it is possible (and easier than implementing yourself) to use a JButton. However, if you can’t use a JButton for any reason, then it makes sense and gives you more control to make your own button.

Does anyone have an example of a “custom” button? Preferably one that doesn’t involve extending JComponent, Component or any of their respective subclasses. Sorry kind of off topic.

Here’s one I wrote just now that uses Java2D:


public class Button {
    private boolean isDisabled, isPressed, isHovering;
    private String text;
    private Font font;
    private Color backgroundColor, hoverColor, pressedColor, textColor;
    private Rectangle2D.Double bounds;
    
    public Button(String text, Font font, int x, int y, int width, int height) {
        this.text = text;
        this.font = font;
        backgroundColor = Color.blue;
        hoverColor = Color.blue.darker();
        pressedColor = Color.blue.darker().darker();
        textColor = Color.black;
        bounds = new Rectangle2D.Double(x,y,width,height):
    }
    
    public void doAction() {
        //Called when clicked.
    }
    
    public void mousePressed(int button, int x, int y) {
        if(isHovering) {
            isHovering = false;
            isPressed = true;
        }
    }
    
    public void mouseReleased(int button, int x, int y) {
        if(bounds.contains(x,y) && isPressed)
            doAction();
        
        isPressed = false;
    }
    
    public void mouseMoved(int x, int y) {
        isHovering = false;
        
        if(bounds.contains(x,y))
            isHovering = true;
    }
    
    public void draw(Graphics2D g) {
        if(isDisabled)
            g.setColor(Color.darkGray);
        else if(isPressed)
            g.setColor(pressedColor);
        else if(isHovering)
            g.setColor(hoverColor);
        else
            g.setColor(backgroundColor);
        g.fill(bounds):
        
        FontMetrics fm = g.getFontMetrics(font);
        int width= fm.stringWidth(text);
        int height = fm.getHeight();
        int x = bounds.x+(bounds.width-width)/2;
        int y = bounds.y+(bounds.height+height)/2+fm.getDescent();
        
        g.setColor(textColor);
        g.drawString(text,x,y);
    }
}

ra4king
I like it! Works perfect for me

I am going to add it to the site!

Glad to help.
Hopefully you understand the code, I don’t want to just spoon feed it to you.

Even if you’re solid on not using LWJGL or Slick2D I still recommend you take a look at the source in them, because they have a lot of great features that you can learn something from. An example is the standard UI-components, like textfields and buttons. I know that Slick2D has a foundation built in for UI, and it already has implemented the basic components. :persecutioncomplex:

Is there a nice way to pull those FontMetrics calculations out of the draw method for your button? It seems silly do be doing them every frame when you would only need to do them once, or when you change the font.

I know there is a deprecated method you can use:

FontMetrics fontMetrics = Toolkit.getDefaultToolkit().getFontMetrics(font);

They’re probably not going to slow down your render loop like you think they are, but one way to not perform them every frame is to have private variables for the data you want to remember (x/y, or whatever), and lazily set them the first iteration through.



    private int x;
    private int y;

    public void draw(Graphics2D g) {
        if(isDisabled)
            g.setColor(Color.darkGray);
        else if(isPressed)
            g.setColor(pressedColor);
        else if(isHovering)
            g.setColor(hoverColor);
        else
            g.setColor(backgroundColor);
        g.fill(bounds):
        
        if (x==0) {
            FontMetrics fm = g.getFontMetrics(font);
            int width= fm.stringWidth(text);
            int height = fm.getHeight();
            x = bounds.x+(bounds.width-width)/2;
            y = bounds.y+(bounds.height+height)/2+fm.getDescent();
        }
        g.setColor(textColor);
        g.drawString(text,x,y);
    }

This is what I used (Scala) and I don’t think it’s deprecated:


    private val gc = GraphicsEnvironment.getLocalGraphicsEnvironment.getDefaultScreenDevice.getDefaultConfiguration
    private val graphics = gc.createCompatibleImage(1, 1).getGraphics

    // For a 12 pt font:
    //    ascent = 12
    //   descent =  3
    //   leading =  0
    //   ------------
    //    height = 15
    protected val fontMetrics12 = graphics.getFontMetrics(new Font("SansSerif", java.awt.Font.PLAIN, 12))
    protected val fontMetrics36 = graphics.getFontMetrics(new Font("SansSerif", java.awt.Font.PLAIN, 36))

In your constructor (or wherever you want to set the X and Y) you can create a temporary BufferedImage and get its Graphics object.