Gbui how to find out if mouse is over an component

Well I have the absolute position of my mouse/cursor, and want to find out if it is over an component or not.

you can add a listener to your component that will fire off when the mouse is over it…you can also add a bss style for some components that is:  componentName:hover {/bss code/}  that will trigger an event like a color change or something along those lines.



There are lots of examples of this in the test directory, lemmie know if you need more help

Well i currently try to implement a steering kinda like freelance (mouse fly) so I need to know if it is over an component at all (including windows, panels,any other gbui element) so using the hover code for just every element i use seems kinda inefficient, i wonder if there is a more usefull general approach here?

If you want it to say allow terrain picking etc, then i use this:



This is in your game loop. I havnt included mouse downs etc, cos i have special uses for them ingame. Therefore youll need to adapt this.




boolean isOnWindow = false;
      for (int i = 0; i < gui.getWindowCount(); i++) {
         BWindow temp = gui.getWindow(i);

         if (!temp.isVisible())
            continue;

         if ((mousePosition.x > temp.getX() && mousePosition.x < temp.getWidth() + temp.getX()) && (mousePosition.y > temp.getY() && mousePosition.y < temp.getHeight() + temp.getY())) {
            isOnWindow = true;
         }

         if (isOnWindow)
            break;
      }


if(!onWindow) {
//PickCode here

}

gui.update(firstMouse1Down, firstMouse2Down, mouse1release, mouse2release, isOnWindow);



And this is a class I use instead of the typical gbui class. Rather than using an action listener you update it from your game loop. Mostly the code is ripped straight out of other places, and i havnt botherd to alter the comments.


import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;

import com.jme.input.InputHandler;
import com.jme.input.KeyInput;
import com.jme.input.KeyInputListener;
import com.jme.input.MouseInput;
import com.jme.input.MouseInputListener;
import com.jme.intersection.CollisionResults;
import com.jme.scene.Spatial;
import com.jme.util.Timer;
import com.jmex.bui.BComponent;
import com.jmex.bui.BRootNode;
import com.jmex.bui.BWindow;
import com.jmex.bui.event.InputEvent;
import com.jmex.bui.event.KeyEvent;
import com.jmex.bui.event.MouseEvent;
import com.jmex.bui.listener.BUpdateListener;

public class GUI extends BRootNode {

   private static GUI gui;

   public static GUI get() {
      return gui;
   }

   public GUI(Timer timer) {
      this(timer, null);

   }

   public GUI(Timer timer, InputHandler handler) {
      _timer = timer;
      _handler = handler;

      KeyInput.get().addListener(_keyListener);

      gui = this;
   }

   public void addMouseListener(MouseInputListener listener) {
      if (mouseListeners == null) {
         mouseListeners = new HashSet<MouseInputListener>();
      }

      mouseListeners.add(listener);
   }

   public void addKeyListener(KeyInputListener listener) {
      if (keyListeners == null) {
         keyListeners = new HashSet<KeyInputListener>();
      }

      keyListeners.add(listener);
   }

   public void addUpdateListener(BUpdateListener listener) {
      if (updateListeners == null) {
         updateListeners = new HashSet<BUpdateListener>();
      }

      updateListeners.add(listener);
   }

   public void fireOnMouseButton(int button, boolean pressed, int x, int y) {
      if (mouseListeners == null) {
         return;
      }
      for (MouseInputListener mouseListener : mouseListeners) {
         mouseListener.onButton(button, pressed, x, y);
      }
   }

   private void fireOnMouseMove(int xDelta, int yDelta, int newX, int newY) {
      if (mouseListeners == null) {
         return;
      }
      for (MouseInputListener mouseListener : mouseListeners) {
         mouseListener.onMove(xDelta, yDelta, newX, newY);
      }
   }

   private void fireOnMouseWheel(int wheelDelta, int x, int y) {
      if (mouseListeners == null) {
         return;
      }
      for (MouseInputListener mouseListener : mouseListeners) {
         mouseListener.onWheel(wheelDelta, x, y);
      }
   }

   private void fireOnKey(char character, int keyCode, boolean pressed) {
      if (keyListeners == null) {
         return;
      }
      for (KeyInputListener keyListener : keyListeners) {
         keyListener.onKey(character, keyCode, pressed);
      }
   }

   private void fireUpdate(BComponent component, float time) {
      if (updateListeners == null) {
         return;
      }
      for (BUpdateListener updateListener : updateListeners) {
         updateListener.update(component, time);
      }
   }

   // documentation inherited
   public long getTickStamp() {
      return _tickStamp;
   }

   // documentation inherited
   public void rootInvalidated(BComponent root) {
      // add the component to the list of invalid roots
      if (!_invalidRoots.contains(root)) {
         _invalidRoots.add(root);
      }
   }

   @Override
   // documentation inherited
   public void updateWorldData(float timePerFrame) {
      super.updateWorldData(timePerFrame);

      // determine our tick stamp in milliseconds
      _tickStamp = _timer.getTime() * 1000 / _timer.getResolution();

      // poll the keyboard and mouse and notify event listeners
      KeyInput.get().update();
      MouseInput.get().update();

      /*// if we have no focus component, update the normal input handler
      if (_focus == null
          && _handler != null) {
          _handler.update(timePerFrame);
      }*/

      fireUpdate(_hcomponent, timePerFrame);

      // if our OpenGL window lost focus, clear our modifiers
      boolean lostFocus = !Display.isActive();
      if (_modifiers != 0 && lostFocus) {
         _modifiers = 0;
      }

      // effect key repeat
      if (_pressed >= 0 && _nextRepeat < _tickStamp) {
         if (lostFocus || !KeyInput.get().isKeyDown(_pressed)) {
            // stop repeating if our window lost focus or for whatever
            // reason we missed the key up event
            _pressed = -1;
         } else {
            // otherwise generate and dispatch a key repeat event
            _nextRepeat += SUBSEQ_REPEAT_DELAY;
            KeyEvent event = new KeyEvent(GUI.this, _tickStamp, _modifiers, KeyEvent.KEY_PRESSED, _presschar, _pressed);
            dispatchEvent(_focus, event);
         }
      }

      // validate all invalid roots
      while (_invalidRoots.size() > 0) {
         BComponent root = _invalidRoots.remove(0);
         // make sure the root is still added to the view hierarchy
         if (root.isAdded()) {
            root.validate();
         }
      }
   }

   @Override
   // documentation inherited
   public float getTooltipTimeout() {
      return (KeyInput.get().isKeyDown(KeyInput.KEY_LCONTROL) || KeyInput.get().isKeyDown(KeyInput.KEY_RCONTROL)) ? 0 : _tipTime;
   }

   /**
    * This listener is notified when a key is pressed or released.
    */
   protected KeyInputListener _keyListener = new KeyInputListener() {
      public void onKey(char character, int keyCode, boolean pressed) {
         // first update the state of the modifiers
         int modifierMask = -1;
         for (int ii = 0; ii < KEY_MODIFIER_MAP.length; ii += 2) {
            if (KEY_MODIFIER_MAP[ii] == keyCode) {
               modifierMask = KEY_MODIFIER_MAP[ii + 1];
               break;
            }
         }
         if (modifierMask != -1) {
            if (pressed) {
               _modifiers |= modifierMask;
            } else {
               _modifiers &= ~modifierMask;
            }
         }

         // generate a key event and dispatch it
         KeyEvent event = new KeyEvent(GUI.this, _tickStamp, _modifiers, pressed ? KeyEvent.KEY_PRESSED : KeyEvent.KEY_RELEASED, character, keyCode);
         boolean processed = dispatchEvent(_focus, event);

         // update our stored list of pressed keys
         if (pressed) {
            _pressed = keyCode;
            _presschar = character;
            _nextRepeat = _tickStamp + INITIAL_REPEAT_DELAY;
         } else {
            if (_pressed == keyCode) {
               _pressed = -1;
            }
         }

         if (!processed) {
            fireOnKey(character, keyCode, pressed);
         }
      }
   };

   public void update(boolean firstMouse1Down, boolean firstMouse2Down, boolean mouse1release, boolean mouse2release, boolean onWindow) {
      MouseInput m = MouseInput.get();

      int x = m.getXAbsolute();
      int y = m.getYAbsolute();
      int xDelta = m.getXDelta();
      int yDelta = m.getYDelta();
      int mDelta = m.getWheelDelta();

      if (firstMouse1Down)
         onButton(0, true, x, y);
      if (firstMouse2Down)
         onButton(1, true, x, y);
      if (mouse1release)
         onButton(0, false, x, y);
      if (mouse2release)
         onButton(1, false, x, y);

      if (mDelta != 0)
         onWheel(mDelta, x, y);

      if (xDelta != 0 || yDelta != 0)
         onMove(xDelta, yDelta, x, y);

   }

   public void onButton(int button, boolean pressed, int x, int y) {
      // recalculate the hover component whenever the a button is pressed
      updateHoverComponent(x, y);

      // if we had no mouse button down previous to this, whatever's
      // under the mouse becomes the "clicked" component (which might be
      // null)
      if (pressed && (_modifiers & ANY_BUTTON_PRESSED) == 0) {
         setFocus(_ccomponent = _hcomponent);
      }

      // update the state of the modifiers
      if (pressed) {
         _modifiers |= MOUSE_MODIFIER_MAP[button];
      } else {
         _modifiers &= ~MOUSE_MODIFIER_MAP[button];
      }

      // generate a mouse event and dispatch it
      boolean processed = dispatchEvent(new MouseEvent(GUI.this, _tickStamp, _modifiers, pressed ? MouseEvent.MOUSE_PRESSED : MouseEvent.MOUSE_RELEASED, button, x, y));

      // finally, if no buttons are up after processing, clear out our
      // "clicked" component
      if ((_modifiers & ANY_BUTTON_PRESSED) == 0) {
         _ccomponent = null;
      }

      if (!processed) {
         fireOnMouseButton(button, pressed, x, y);
      }
   }

   public void onMove(int xDelta, int yDelta, int newX, int newY) {
      mouseDidMove(newX, newY);
      boolean processed = dispatchEvent(new MouseEvent(GUI.this, _tickStamp, _modifiers, _ccomponent != null ? MouseEvent.MOUSE_DRAGGED : MouseEvent.MOUSE_MOVED, newX, newY));

      if (!processed) {
         fireOnMouseMove(xDelta, yDelta, newX, newY);
      }
   }

   public void onWheel(int wheelDelta, int x, int y) {
      boolean processed = dispatchEvent(new MouseEvent(GUI.this, _tickStamp, _modifiers, MouseEvent.MOUSE_WHEELED, -1, x, y, wheelDelta));
      updateHoverComponent(x, y);

      if (!processed) {
         fireOnMouseWheel(wheelDelta, x, y);
      }
   }

   protected boolean dispatchEvent(MouseEvent event) {
      return GUI.this.dispatchEvent(_ccomponent != null ? _ccomponent : _hcomponent, event);
   }

   protected long _tickStamp;
   protected Timer _timer;
   protected InputHandler _handler;
   protected ArrayList<BComponent> _invalidRoots = new ArrayList<BComponent>();

   /**
    * This is used for key repeat.
    */
   protected int _pressed = -1;

   /**
    * This is used for key repeat.
    */
   protected char _presschar;

   /**
    * This is used for key repeat.
    */
   protected long _nextRepeat;

   /**
    * Maps key codes to modifier flags.
    */
   protected static final int[] KEY_MODIFIER_MAP = { KeyInput.KEY_LSHIFT,
         InputEvent.SHIFT_DOWN_MASK,
         KeyInput.KEY_RSHIFT,
         InputEvent.SHIFT_DOWN_MASK,
         KeyInput.KEY_LCONTROL,
         InputEvent.CTRL_DOWN_MASK,
         KeyInput.KEY_RCONTROL,
         InputEvent.CTRL_DOWN_MASK,
         KeyInput.KEY_LMENU,
         InputEvent.ALT_DOWN_MASK,
         KeyInput.KEY_RMENU,
         InputEvent.ALT_DOWN_MASK,
         KeyInput.KEY_LWIN,
         InputEvent.META_DOWN_MASK,
         KeyInput.KEY_RWIN,
         InputEvent.META_DOWN_MASK, };

   /**
    * Maps button indices to modifier flags.
    */
   protected static final int[] MOUSE_MODIFIER_MAP = { InputEvent.BUTTON1_DOWN_MASK, InputEvent.BUTTON2_DOWN_MASK, InputEvent.BUTTON3_DOWN_MASK, };

   /**
    * Used to check whether any button remains pressed.
    */
   protected static final int ANY_BUTTON_PRESSED = InputEvent.BUTTON1_DOWN_MASK | InputEvent.BUTTON2_DOWN_MASK | InputEvent.BUTTON3_DOWN_MASK;

   protected static final long INITIAL_REPEAT_DELAY = 400L;
   protected static final long SUBSEQ_REPEAT_DELAY = 30L;

   private Set<MouseInputListener> mouseListeners;
   private Set<KeyInputListener> keyListeners;
   private Set<BUpdateListener> updateListeners;

}


Ok thanks, will try to implement it later :slight_smile:

Do you have a small segment of code that you used to implement this class?  I'm implementing it now, I just want to make sure that when I do the integration tests I've not missed something that I should have (like extending SimpleGame as opposed to BaseGame or even a GameState or something.

Yo sure, nps atall. Its all pretty hacky, But it seems to work.



gui = new GUI(timer, inputHandler);
orthoNode.attachChild(gui);



Above is the creation code, and below is the mouse code. Other than that, I think I included everything. If you are missing anything let me know. When I implemented this I was just extending a simple pass game, but i really see no reason why there would be any issues in gamestates.

If you are integrating it into GBUI, it might be nice to clean it up a bit and maybe create a subclass for PolledRootNode and this, as theres alot of duplicate code. And also maybe include the mouse stuff within it.


mouse1release = false;
      mouse2release = false;
      firstMouse1Down = false;
      firstMouse2Down = false;
      if (MouseInput.get().isButtonDown(0)) {
         if (!mouse1down)
            firstMouse1Down = true;
         else
            firstMouse1Down = false;
         mouse1down = true;
      }

      if (MouseInput.get().isButtonDown(1)) {
         if (!mouse2down)
            firstMouse2Down = true;
         else
            firstMouse2Down = false;
         mouse2down = true;
      }

      if (mouse1down && !MouseInput.get().isButtonDown(0)) {
         mouse1down = false;
         mouse1release = true;
      }
      if (mouse2down && !MouseInput.get().isButtonDown(1)) {
         mouse2down = false;
         mouse2release = true;
      }

I'll take a look…at the bare minimum I wanted to add what you had to an example class so that it would be documented - even if it's a wiki entry.  I might add a DebugPolledRootNode class or something that utilizes this or something along those lines.