Custom buttons, only one will take mouse movement.

well, as the title says, I have a Button class


package com.gamesunknown.engine.GUI;


/**
 * A button that can be placed anywhere and can be any shape.
 * 
 * @author (h3ckboy aka Pearce Keesling) 
 * @version (a version number or a date)
 */
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.Component;

import javax.swing.JApplet;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JToolTip;
import javax.swing.Timer;
import javax.swing.SwingUtilities;
public class Button extends Component implements MouseListener, MouseMotionListener
{
  Color ACTIVE_COLOR = Color.red;

  Color INACTIVE_COLOR = Color.lightGray;

  protected String text;

  protected Rectangle rectangle;

  protected boolean isActive;
  
  Color TextColor = Color.black;
  //Color TextColorActive = Color.black;
  
  protected ActionListener actionListener;

  protected static Button button;
    public Button(Rectangle r, String text)
    {
        try{
            this.rectangle = r;
    setText(text);
    setName(text);
    //setOpaque(false);

    addMouseListener(this);
    addMouseMotionListener(this);
    System.out.println("there are " + getMouseListeners().length + " mouse listeners");
}catch(Exception e){System.out.println(e);}
    /*rectangle = new Rectangle(polygon.getBounds()); // Bug alert!
    rectangle.grow(1, 1);

    setBounds(rectangle);
    polygon.translate(-rectangle.x, -rectangle.y);*/
    }
      public void setText(String t) {
    text = t;
  }

  public String getText() {
    return text;
  }

  public void mouseMoved(MouseEvent e) {
      System.out.println("well the mouse movemnet is being registered by the button named "+text);
    if (!rectangle.contains(e.getX(), e.getY()) || e.isConsumed()) {
      if (isActive) {
        isActive = false;
        repaint();
      }
      return; // quickly return, if outside our rectangle
    }
    //System.out.println("mouse is over the button");
    int x = e.getX();
    int y = e.getY();
    boolean active = rectangle.contains(x, y);

    if (isActive != active)
      setState(active);
    if (active)
      e.consume();
  }

  public void mouseDragged(MouseEvent e) {
  }

  protected void setState(boolean active) {
    isActive = active;
    repaint();
    if (active) {
      if (button != null)
        button.setState(false);
      setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
    } else {
      button = null;
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }
  }

  public void mouseClicked(MouseEvent e) {
      if(rectangle.contains(e.getX(),e.getY()))
      {
//          ActionEvent event = new ActionEvent(this,0,text);
          actionListener.actionPerformed(new ActionEvent(this,0,text));
        }
  }

  public void mousePressed(MouseEvent e) {
  }

  public void mouseReleased(MouseEvent e) {
  }

  public void mouseExited(MouseEvent e) {
    mouseMoved(e);
  }

  public void mouseEntered(MouseEvent e) {
    mouseMoved(e);
  }

  public void paint(Graphics g) {
    g.setColor(isActive ? ACTIVE_COLOR : INACTIVE_COLOR);
    //g.drawPolygon(polygon);
    //Rectangle r = polygon.getBounds();
    g.fillRect((int)rectangle.getX(),(int)rectangle.getY(),(int)rectangle.getWidth(),(int)rectangle.getHeight());
    int length = SwingUtilities.computeStringWidth( g.getFontMetrics(), text );  
    g.setColor(TextColor);
    g.drawString(text,(int)(rectangle.getX()+rectangle.getWidth()/2)-length/2,(int)(rectangle.getY()+rectangle.getHeight()/2));
    //System.out.println("rendering button named : "+text);
    System.out.println("there are " + getMouseListeners().length + " mouse listeners");
  }
  public void addActionListener(ActionListener a)
  {
      this.actionListener = a;
    }
  public void setColor(Color Active,Color Inactive)
  {
      this.ACTIVE_COLOR = Active;
      this.INACTIVE_COLOR = Inactive;
    }
  public void setTextColor(Color TextColor)
  {
      this.TextColor = TextColor;
    }
}

and a MenuState class


package States;


/**
 * The state with the menu
 * 
 * @author (h3ckboy Pearce Keesling) 
 */
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
/**My Game library imports*/
import com.gamesunknown.engine.Engine;
import com.gamesunknown.engine.State;
import com.gamesunknown.engine.GUI.Button;
public class MenuState extends State implements ActionListener
{
    JApplet applet;
    Container container;
    Engine engine;
    public static int ID;
    boolean first = true;
    Button[] buttons = new Button[2];
    public MenuState(JApplet applet, Engine engine)
    {
        super();
        this.applet = applet;
        this.engine = engine;
        container = applet.getContentPane();
    }
    public void init()
    {
        //play button
        buttons[0] = new Button(new Rectangle(200,10,50,50), "Play");
        buttons[0].addActionListener(this);
        applet.getContentPane().add(buttons[0]);
        container.validate();
        //quit button
        buttons[1] = new Button(new Rectangle(200,150,50,50),"Quit");
        buttons[1].addActionListener(this);
        applet.getContentPane().add(buttons[1]);
        container.validate();
        //System.out.println("there are " + container.getMouseListeners().length + " mouse listeners");
        first = true;
    }
/**MAIN LOOP STUFF*/
//rendering stuff
    public void render(Graphics g){
        if(first){init();first = false;}
        container.update(g);
        container.paint(g);
        //container.paintComponents(g);
        //renderButtons(g);
        //System.out.println(container.getComponentAt(200,150).getName());
    }
    public void renderButtons(Graphics g)
    {
        for(int i = 0; i<buttons.length;i++)
        {
            container.removeAll();
            container.add(buttons[i]);
            container.paint(g);
        }
    }
    public void update()
    {
    }
    public void exitState()
    {
        first = true;
        container.removeAll();
    }
/**GUI ACTION PERFORMED*/
    public void ActionFerformed(String source)
    {
        System.out.println("you pressed the button named : "+source);
    }
    public void actionPerformed(ActionEvent e)
    {
        System.out.println("there was an action performed : "+e.getActionCommand());
        if(e.getActionCommand().equals("Play"))
        {
            System.out.println("time to play the game!");
        }
    }
}

when I only put in one button, there is not a single problem, but when I put in 2, only the first will take mouse movement…

I know I am being a bit vague, but I dont know how ot explain it better.

there are some imports which are for a simple game engine I am making for myself(might make a topic later), and well, the buttons are just me working on the engine.

It’s a focus problem. Only one component can have focus at a time (which is either changed manually or typically via a click or tab press), and only the focused component can register mouse movement/presses.

So, unfortunately you’ll have to rethink your strategy (maybe put a single mouse listener in the main component and then send events to all contained components, and constantly request focus with the main component). Probably you shouldn’t be using Component at all, and should just write your own from scratch. Otherwise, what’s the purpose?

finally a reason! I have spent hours trying different things.

would it be really bad if I just called the paint twice, but in between change the focus?

EDIT: now that that I think about that, doesnt quite seem like a very efficient idea for more than 1-2 buttons.

how are you proposing I write it from scratch, just render it directly to the applet? but how do I then do the mouse listening, would it just be as if I was doing it ina regular game? I will try this, but please correct me if htis wont work.

Yeah, basically you’d draw all the buttons yourself wherever you wanted them to go within their parent component (Applet or JFrame or JPanel or whatever). Then the parent component receives mouse events, and if it find a mouse event to be within the bounds of any of the buttons, it forwards that mouse event to the button. Alternatively, you can create a new mouse event with mouse coordinates local to the button instead of global ones (might be easier if the buttons don’t know their own positions), or just go completely event-less and simply do something like myButton.mouseMoved(int x, int y).

ok thank you, I had messed around with this earlier but hadnt put to much time and effort into it because I was not very confident of this working.

I think that since I am bothering making an engine, to simplify things for later, I am going to try and make a Panel class which will work like a jpanel, and that will forward the mouse events to each of the buttons.

well, I figured I should tell you, I got it working :slight_smile:

thx again demonpants :wink:

Nice. :slight_smile:

No problem at all.