GBUI: Input boxes strange default behavior in applets

I've started to work on some interface for my application, and I'm using a couple of BTextFields. Their default input behavior for applets (using a CanvasRootNode) seems very strange to me, so I'm wondering if I'm doing something wrong, especially since everything listed here works fine for SimpleGame tests:


  1. They seem to receive input when the mouse is over them, not when the mouse has been clicked over them (losing focus when the mouse exits).


  2. Input focus and cursor-focus which is gained via requestFocus() is different, and when the BTextField accepts input, it doesn't get a cursor to indicate where the typing will print to.


  3. Similarly, requestFocus() doesn't grant input focus, and only creates a cursor. So, even if I add a listener to requestFocus() for the BTextField when the mouse has been clicked over it, if the mouse exits the BTextField, the BTextField no longer receives input.


  4. Service keys like "Shift" are being processed as input and displayed as boxes.



    This can all be coded around, but the coding around seems strange or unpleasant (for example, adding a custom listener to the text field to iterate all windows, see which one the mouse is over, see which component the mouse is over and call requestFocus() for it - this is what I'm currently using to make the cursor visible).



    Is there a good way to make things work differently?



    Just in case, here's some test code:



public class Test extends SimpleJMEPassApplet
// Need to have glCanvas modifier protected for this test to work (by default, it is private in jme2)
{
   public void simpleAppletSetup()
   {      
      BuiSystem.init(new CanvasRootNode(glCanvas), "/rsrc/style2.bss");
      BuiSystem.getRootNode().addWindow(new RegisterWindow("Register window", BuiSystem.getStyle(), new BorderLayout()));
      getRootNode().attachChild(BuiSystem.getRootNode());
   }
}




And a simple register window:


import com.jme.renderer.ColorRGBA;
import com.jmex.bui.BButton;
import com.jmex.bui.BButtonBar;
import com.jmex.bui.BComponent;
import com.jmex.bui.BContainer;
import com.jmex.bui.BLabel;
import com.jmex.bui.BPasswordField;
import com.jmex.bui.BStyleSheet;
import com.jmex.bui.BTextField;
import com.jmex.bui.BTitleBar;
import com.jmex.bui.BWindow;
import com.jmex.bui.BuiSystem;
import com.jmex.bui.background.BBackground;
import com.jmex.bui.background.TintedBackground;
import com.jmex.bui.enumeratedConstants.TitleOptions;
import com.jmex.bui.event.ActionEvent;
import com.jmex.bui.event.ActionListener;
import com.jmex.bui.event.ComponentListener;
import com.jmex.bui.event.MouseEvent;
import com.jmex.bui.event.MouseListener;
import com.jmex.bui.layout.AbsoluteLayout;
import com.jmex.bui.layout.BLayoutManager;
import com.jmex.bui.layout.BorderLayout;
import com.jmex.bui.layout.GroupLayout;
import com.jmex.bui.util.Rectangle;

public class RegisterWindow extends DraggableWindow
{

   public RegisterWindow(String name, BStyleSheet style, BLayoutManager layout)
   {
      super(name, style, layout);
      init();
   }
   
   public RegisterWindow(BStyleSheet style, BLayoutManager layout)
   {
      super(style, layout);
      init();
   }
   
   public void init()
   {
      // Add a title bar and an absolute component area to place components
      BTitleBar tb = new BTitleBar("RegisterLabel", new BLabel("Choose your login and password: "),   TitleOptions.NONE);
      this.add(tb, BorderLayout.NORTH);      
      BContainer componentArea = new BContainer("Input box container", new AbsoluteLayout());      
      this.add(componentArea, BorderLayout.CENTER);
      
      // Also add an "OK" button at south
      BButton b = new BButton("Register");   
      BButtonBar bar = new BButtonBar("RegisterButtonBar", null);
      GroupLayout.Constraints constraints = new GroupLayout.Constraints(false);
      bar.add(b, constraints);
      this.add(bar, BorderLayout.SOUTH);      
      
      /** Requests focus for clicked input boxes*/
      MouseListener focusGainer = new MouseListener()
      {
         public void mouseEntered(MouseEvent event){}
         public void mouseExited(MouseEvent event){}
         public void mousePressed(MouseEvent event){}
         @Override
         public void mouseReleased(MouseEvent event)
         {
            int x0 = event.getX(), y0 = event.getY();
            for(BWindow win : BuiSystem.getRootNode().getAllWindows())
            {
               int x = win.getAbsoluteX(), y = win.getAbsoluteY();
               int w = win.getWidth(), h = win.getHeight();
               if(x0 > x && x0 < x + w && y0 > y && y0 < y + h)
               {
                  win.getHitComponent(x0, y0).requestFocus();                  
                  break;
               }               
            }            
      }};
      
      // Add login and password field
      final BTextField login = new BTextField("Login");            
      componentArea.add(login, new Rectangle(20,100,325,30));
      login.addListener(new ComponentListener(){});
      final BPasswordField password = new BPasswordField("");
      componentArea.add(password, new Rectangle(20,60,325,30));
      
      login.getEffectColor().g = 0;
      login.addListener(focusGainer);
      login.setConsumeMouseEvents(true);
      password.addListener(focusGainer);
      
      // Initialize exterior
      this.setSize(400, 200);
      this.center();
      BBackground background = new TintedBackground(new ColorRGBA(0.4f,0.3f,0.3f,0.9f));
      this.setBackground(this.getState(), background);
      this.setConsumeMouseEvents(false);      
      

      // Initialize registration action
      ActionListener OKListener = new ActionListener()
      {      
         @Override
         public void actionPerformed(ActionEvent event)
         {
            if(event.getSource() instanceof BComponent)
            {
               System.out.println(login.getText());
               System.out.println(password.getText());
            }            
         }
      };
      b.addListener(OKListener);
   }
   
}



And the same window working with SimpleGame:


import gui.RegisterWindow;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.jmex.bui.BuiSystem;
import com.jmex.bui.layout.BorderLayout;

public class TestApplication extends BaseTest2 { // BaseTest2 from the gbui trunk: test/java/com.jmex.bui.base
    protected void createWindows()
    {
       BuiSystem.getRootNode().addWindow(new RegisterWindow("Register window", BuiSystem.getStyle(), new BorderLayout()));   
    }

    public static void main(String[] args) {     
        new TestApplication.start();
    }
}

interesting.  I'll look at this today and see if I can come up with something

So how did it go?  :wink:

I missed all last week from being sick.  I'll look at this today.

ok, so I've looked at this some and had to fix a few code issues before it would even try to compile.



glCanvas still has private access for me (I'm checking out the latest trunk now).



that's the only unresolved part right now.  As soon as the svn is checked out and I compile the new jars, I'll try again.



Have you any updates you've made to your code that I need to know about?



thx



timo

standtrooper said:

glCanvas still has private access for me (I'm checking out the latest trunk now).


It's private in the trunk of jme, but as far as I could tell, the only way to initialize for an applet is to pass the canvas  to the appropriate method (this was also true when I tried FengGUI, with jme1), so what I do is copy the SimpleJMEPassApplet to my project and change the "private" modifier to "protected". I couldn't find any information on how to do this any other way, and this one seems to work, so I've been going along with it...

I have some other changes, but they seem to be irrelevant to compiling and the errors mentioned in the original post (I just checked, by simply replacing the modifier in SimpleJMEPassApplet in the checked out version and inheriting from that).

Do you mean code issues with the code I posted? I copied it to a new project, it seems to work for me... (of course, if there is access to glCanvas...).

standtrooper said:

Have you any updates you've made to your code that I need to know about?

Actually, I forgot to mention, that I'm using gbui with the corrections from a previous post about gbui in applets,
http://www.jmonkeyengine.com/jmeforum/index.php?topic=9908.msg75767#msg75767
But the UI should work fine with out them, these correction seem to only remove exceptions that are being thrown.

Everything else appears to be in sync with the svn, though if it's relevant it might be necessary for me to update, I think my version is like 1 month old.

This is the applet base I'm using, just in case:


import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.KeyListener;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseWheelListener;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.jme.input.FirstPersonHandler;
import com.jme.input.InputHandler;
import com.jme.input.InputSystem;
import com.jme.input.KeyBindingManager;
import com.jme.input.KeyInput;
import com.jme.input.MouseInput;
import com.jme.light.PointLight;
import com.jme.math.Vector3f;
import com.jme.renderer.Camera;
import com.jme.renderer.ColorRGBA;
import com.jme.renderer.Renderer;
import com.jme.renderer.pass.BasicPassManager;
import com.jme.scene.Node;
import com.jme.scene.Spatial;
import com.jme.scene.Text;
import com.jme.scene.shape.Quad;
import com.jme.scene.state.LightState;
import com.jme.scene.state.WireframeState;
import com.jme.system.DisplaySystem;
import com.jme.system.canvas.JMECanvas;
import com.jme.system.canvas.SimplePassCanvasImpl;
import com.jme.util.Debug;
import com.jme.util.GameTaskQueue;
import com.jme.util.GameTaskQueueManager;
import com.jme.util.TextureManager;
import com.jme.util.stat.StatCollector;
import com.jme.util.stat.StatType;
import com.jme.util.stat.graph.GraphFactory;
import com.jme.util.stat.graph.LineGrapher;
import com.jme.util.stat.graph.TabledLabelGrapher;
import com.jmex.awt.input.AWTKeyInput;
import com.jmex.awt.input.AWTMouseInput;
import com.jmex.awt.lwjgl.LWJGLAWTCanvasConstructor;

public class Mol3dJMEPassApplet extends Applet
{
   private static final Logger logger = Logger.getLogger(Mol3dJMEPassApplet.class.getName());
   
   private static final long serialVersionUID = 1L;
   
   protected Canvas glCanvas;
   private SimpleAppletCanvasImplementor impl;
   
   private static final String INIT_LOCK = "INIT_LOCK";
   
   protected static final int STATUS_INITING = 0;
   protected static final int STATUS_RUNNING = 1;
   protected static final int STATUS_DESTROYING = 2;
   protected static final int STATUS_DEAD = 3;
   
   
   private static final String USE_APPLET_CANVAS_SIZE = "useAppletCanvasSize";
   private static final int DEFAULT_JME_CANVAS_WIDTH = 640;
   private static final int DEFAULT_JME_CANVAS_HEIGHT = 480;
   
   protected int status = STATUS_INITING;
   
   /**
    * Alpha bits to use for the renderer. Must be set in the constructor.
    */
   protected int alphaBits = 0;
   
   /**
    * Depth bits to use for the renderer. Must be set in the constructor.
    */
   protected int depthBits = 8;
   
   /**
    * Stencil bits to use for the renderer. Must be set in the constructor.
    */
   protected int stencilBits = 0;
   
   /**
    * Number of samples to use for the multisample buffer. Must be set in the
    * constructor.
    */
   protected int samples = 0;
   
   @Override
   public void init()
   {
      logger.info("Initializing applet. Attempting to synchronize.");
      synchronized (INIT_LOCK)
      {
         logger.info("Synchronized.");
         
         TextureManager.clearCache();
         Text.resetFontTexture();
         
         DisplaySystem display = DisplaySystem.getDisplaySystem();
         display.registerCanvasConstructor("AWT", LWJGLAWTCanvasConstructor.class);
         display.setMinDepthBits(depthBits);
         display.setMinStencilBits(stencilBits);
         display.setMinAlphaBits(alphaBits);
         display.setMinSamples(samples);
         
         int canvasWidth;
         int canvasHeight;
         /**
          * Check if we're using the applet's specified dimensions or the
          * default.
          */
         if (Boolean.parseBoolean(this.getParameter(USE_APPLET_CANVAS_SIZE)))
         {
            canvasWidth = getWidth();
            canvasHeight = getHeight();
         } else
         {
            canvasWidth = DEFAULT_JME_CANVAS_WIDTH;
            canvasHeight = DEFAULT_JME_CANVAS_HEIGHT;
         }
         glCanvas = (Canvas) DisplaySystem.getDisplaySystem().createCanvas(canvasWidth, canvasHeight);
         
         // Important! Here is where we add the guts to the canvas:
         impl = new SimpleAppletCanvasImplementor(getWidth(), getHeight());
         
         ((JMECanvas) glCanvas).setImplementor(impl);
         setLayout(new BorderLayout());
         add(glCanvas, BorderLayout.CENTER);
         
         glCanvas.addComponentListener(new ComponentAdapter()
         {
            public void componentResized(ComponentEvent ce)
            {
               if (impl != null)
               {
                  impl.resizeCanvas(glCanvas.getWidth(), glCanvas.getHeight());
                  if (impl.getCamera() != null)
                  {
                     Callable<?> exe = new Callable<Object>()
                     {
                        public Object call()
                        {
                           impl.getCamera().setFrustumPerspective(45.0f,
                                 (float) glCanvas.getWidth() / (float) glCanvas.getHeight(), constants.Numerical.zNear, constants.Numerical.zFar);
                           return null;
                        }
                     };
                     GameTaskQueueManager.getManager().getQueue(GameTaskQueue.RENDER).enqueue(exe);
                  }
               }
            }
         });
         
         glCanvas.setFocusable(true);
         glCanvas.addFocusListener(new FocusListener()
         {
            
            public void focusGained(FocusEvent arg0)
            {
               ((AWTKeyInput) KeyInput.get()).setEnabled(true);
               ((AWTMouseInput) MouseInput.get()).setEnabled(true);
            }
            
            public void focusLost(FocusEvent arg0)
            {
               ((AWTKeyInput) KeyInput.get()).setEnabled(false);
               ((AWTMouseInput) MouseInput.get()).setEnabled(false);
            }
            
         });
         
         // We are going to use jme's Input systems, so enable updating.
         ((JMECanvas) glCanvas).setUpdateInput(true);
         
         if (!KeyInput.isInited()) KeyInput.setProvider(InputSystem.INPUT_SYSTEM_AWT);
         ((AWTKeyInput) KeyInput.get()).setEnabled(false);
         KeyListener kl = (KeyListener) KeyInput.get();
         
         glCanvas.addKeyListener(kl);
         
         if (!MouseInput.isInited()) MouseInput.setProvider(InputSystem.INPUT_SYSTEM_AWT);
         ((AWTMouseInput) MouseInput.get()).setEnabled(false);
         ((AWTMouseInput) MouseInput.get()).setDragOnly(true);
         glCanvas.addMouseListener((MouseListener) MouseInput.get());
         glCanvas.addMouseWheelListener((MouseWheelListener) MouseInput.get());
         glCanvas.addMouseMotionListener((MouseMotionListener) MouseInput.get());
         glCanvas.addMouseMotionListener(new MouseMotionAdapter()
         {
            public void mouseMoved(java.awt.event.MouseEvent e)
            {
               //if (!glCanvas.hasFocus()) glCanvas.requestFocus();
            };
         });
      }
      logger.info("Finished initialization.");
   }
   
   
   public void simpleAppletSetup()
   {
   }
   
   public void simpleAppletUpdate()
   {
   }
   
   public void simpleAppletRender()
   {
   }
   
   public Camera getCamera()
   {
      return impl.getCamera();
   }
   
   public Renderer getRenderer()
   {
      return impl.getRenderer();
   }
   
   public Node getRootNode()
   {
      return impl.getRootNode();
   }
   
   public Node getStatNode()
   {
      return impl.getStatNode();
   }
   
   public float getTimePerFrame()
   {
      return impl.getTimePerFrame();
   }
   
   public LightState getLightState()
   {
      return impl.getLightState();
   }
   
   public WireframeState getWireframeState()
   {
      return impl.getWireframeState();
   }
   
   public InputHandler getInputHandler()
   {
      return impl.getInputHandler();
   }
   
   public void setInputHandler(InputHandler input)
   {
      impl.setInputHandler(input);
   }
   
   public BasicPassManager getManager()
   {
      return impl.getManager();
   }
   
   class SimpleAppletCanvasImplementor extends SimplePassCanvasImpl
   {
      
      /**
       * True if the renderer should display the depth buffer.
       */
      protected boolean showDepth = false;      
      /**
       * True if the renderer should display bounds.
       */
      protected boolean showBounds = false;      
      /**
       * True if the rnederer should display normals.
       */
      protected boolean showNormals = false;      
      protected boolean pause;      
      /**
       * A wirestate to turn on and off for the rootNode
       */
      protected WireframeState wireState;      
      private InputHandler input;      
      /**
       * A lightstate to turn on and off for the rootNode
       */
      protected LightState lightState;      
      /**
       * The root node of our stat graphs.
       */
      protected Node statNode;      
      private TabledLabelGrapher tgrapher;
      private Quad labGraph;
      
      protected SimpleAppletCanvasImplementor(int width, int height)
      {
         super(width, height);
      }      
      public Node getStatNode()
      {
         return statNode;
      }      
      public LightState getLightState()
      {
         return lightState;
      }      
      public WireframeState getWireframeState()
      {
         return wireState;
      }      
      public InputHandler getInputHandler()
      {
         return input;
      }      
      public void setInputHandler(InputHandler input)
      {
         this.input = input;
      }
      public void simpleUpdate()
      {
         
         input.update(tpf);
         
         if (Debug.stats)
         {
            StatCollector.update();
            labGraph.setLocalTranslation(.5f * labGraph.getWidth(), (renderer.getHeight() - .5f * labGraph.getHeight()), 0);
         }
         
         simpleAppletUpdate();
         
         /** If toggle_pause is a valid command (via key p), change pause. */
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_pause", false))
         {
            pause = !pause;
         }
         
         /**
          * If toggle_wire is a valid command (via key T), change wirestates.
          */
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_wire", false))
         {
            wireState.setEnabled(!wireState.isEnabled());
            rootNode.updateRenderState();
         }
         /**
          * If toggle_lights is a valid command (via key L), change
          * lightstate.
          */
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_lights", false))
         {
            lightState.setEnabled(!lightState.isEnabled());
            rootNode.updateRenderState();
         }

         
         /** If toggle_depth is a valid command (via key F3), change depth. */
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_depth", false))
         {
            showDepth = !showDepth;
         }
         
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("toggle_normals", false))
         {
            showNormals = !showNormals;
         }
         
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("screen_shot", false))
         {
            renderer.takeScreenShot("SimpleAppletScreenShot");
         }
         
         if (KeyBindingManager.getKeyBindingManager().isValidCommand("mem_report", false))
         {
            long totMem = Runtime.getRuntime().totalMemory();
            long freeMem = Runtime.getRuntime().freeMemory();
            long maxMem = Runtime.getRuntime().maxMemory();
            
            logger.info("|*|*|  Memory Stats  |*|*|");
            logger.info("Total memory: " + (totMem >> 10) + " kb");
            logger.info("Free memory: " + (freeMem >> 10) + " kb");
            logger.info("Max memory: " + (maxMem >> 10) + " kb");
         }
      }
      
      
      
      public void simpleSetup()
      {
         logger.info("Setting up jMonkey applet. Attempting to synchronize.");
         synchronized (INIT_LOCK)
         {
            logger.info("Synchronized.");
            input = new FirstPersonHandler(getCamera(), 50, 1);
            
            /**
             * Create a wirestate to toggle on and off. Starts disabled with
             * default width of 1 pixel.
             */
            wireState = renderer.createWireframeState();
            wireState.setEnabled(false);
            
            rootNode.setRenderState(wireState);
            
            // ---- LIGHTS
            /** Set up a basic, default light. */
            PointLight light = new PointLight();
            light.setDiffuse(new ColorRGBA(0.75f, 0.75f, 0.75f, 0.75f));
            light.setAmbient(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
            light.setLocation(new Vector3f(100, 100, 100));
            light.setEnabled(true);
            
            /**
             * Attach the light to a lightState and the lightState to
             * rootNode.
             */
            lightState = renderer.createLightState();
            lightState.setEnabled(true);
            lightState.attach(light);
            rootNode.setRenderState(lightState);
            
            // Finally, a stand alone node (not attached to root on purpose)
            statNode = new Node("FPS node");
            statNode.setCullHint(Spatial.CullHint.Never);
            
            if (Debug.stats)
            {
               setupStatGraphs();
               setupStats();
            }
            
            statNode.updateGeometricState(0, true);
            statNode.updateRenderState();
            
            try
            {
               simpleAppletSetup();
            } catch (Exception e)
            {
               // Had issues setting up. We'll catch it and go on so it
               // doesn't
               // try setting up over and over.
               logger.logp(Level.SEVERE, this.getClass().toString(), "simpleSetup()", "Exception", e);
            }
            
            /** Assign key P to action "toggle_pause". */
            KeyBindingManager.getKeyBindingManager().set("toggle_pause", KeyInput.KEY_P);
            /** Assign key T to action "toggle_wire". */
            KeyBindingManager.getKeyBindingManager().set("toggle_wire", KeyInput.KEY_T);
            /** Assign key L to action "toggle_lights". */
            KeyBindingManager.getKeyBindingManager().set("toggle_lights", KeyInput.KEY_L);
            /** Assign key B to action "toggle_bounds". */
            KeyBindingManager.getKeyBindingManager().set("toggle_bounds", KeyInput.KEY_B);
            /** Assign key N to action "toggle_normals". */
            KeyBindingManager.getKeyBindingManager().set("toggle_normals", KeyInput.KEY_N);
            KeyBindingManager.getKeyBindingManager().set("screen_shot", KeyInput.KEY_F1);
            KeyBindingManager.getKeyBindingManager().set("exit", KeyInput.KEY_ESCAPE);
            KeyBindingManager.getKeyBindingManager().set("mem_report", KeyInput.KEY_R);
            
            status = STATUS_RUNNING;
         }
         logger.info("Complete jMonkey applet setup.");
      }      
      public void simpleRender()
      {
         simpleAppletRender();
         statNode.draw(renderer);
      }
      /**
       * Set up which stats to graph
       *
       */
      protected void setupStats()
      {
         tgrapher.addConfig(StatType.STAT_FRAMES, LineGrapher.ConfigKeys.Color.name(), ColorRGBA.green);
         tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT, LineGrapher.ConfigKeys.Color.name(), ColorRGBA.cyan);
         tgrapher.addConfig(StatType.STAT_QUAD_COUNT, LineGrapher.ConfigKeys.Color.name(), ColorRGBA.lightGray);
         tgrapher.addConfig(StatType.STAT_LINE_COUNT, LineGrapher.ConfigKeys.Color.name(), ColorRGBA.red);
         tgrapher.addConfig(StatType.STAT_GEOM_COUNT, LineGrapher.ConfigKeys.Color.name(), ColorRGBA.gray);
         tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS, LineGrapher.ConfigKeys.Color.name(), ColorRGBA.orange);
         
         tgrapher.addConfig(StatType.STAT_FRAMES, TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
         tgrapher.addConfig(StatType.STAT_FRAMES, TabledLabelGrapher.ConfigKeys.Name.name(), "Frames/s:");
         tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT, TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
         tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT, TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Tris:");
         tgrapher.addConfig(StatType.STAT_TRIANGLE_COUNT, TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
         tgrapher.addConfig(StatType.STAT_QUAD_COUNT, TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
         tgrapher.addConfig(StatType.STAT_QUAD_COUNT, TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Quads:");
         tgrapher.addConfig(StatType.STAT_QUAD_COUNT, TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
         tgrapher.addConfig(StatType.STAT_LINE_COUNT, TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
         tgrapher.addConfig(StatType.STAT_LINE_COUNT, TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Lines:");
         tgrapher.addConfig(StatType.STAT_LINE_COUNT, TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
         tgrapher.addConfig(StatType.STAT_GEOM_COUNT, TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
         tgrapher.addConfig(StatType.STAT_GEOM_COUNT, TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Objs:");
         tgrapher.addConfig(StatType.STAT_GEOM_COUNT, TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
         tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS, TabledLabelGrapher.ConfigKeys.Decimals.name(), 0);
         tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS, TabledLabelGrapher.ConfigKeys.Name.name(), "Avg.Tex binds:");
         tgrapher.addConfig(StatType.STAT_TEXTURE_BINDS, TabledLabelGrapher.ConfigKeys.FrameAverage.name(), true);
      }
      /**
       * Set up the graphers we will use and the quads we'll show the stats
       * on.
       *
       */
      protected void setupStatGraphs()
      {
         StatCollector.setSampleRate(1000L);
         StatCollector.setMaxSamples(30);
         labGraph = new Quad("labelGraph", Math.max(renderer.getWidth() / 3, 250), 55)
         {
            private static final long serialVersionUID = 1L;
            @Override
            public void draw(Renderer r)
            {
               StatCollector.pause();
               super.draw(r);
               StatCollector.resume();
            }
         };
         tgrapher = GraphFactory.makeTabledLabelGraph((int) labGraph.getWidth(), (int) labGraph.getHeight(), labGraph);
         tgrapher.setColumns(1);
         labGraph.setLocalTranslation(0, (renderer.getHeight() * 5 / 6), 0);
         statNode.attachChild(labGraph);
         
      }
   }
}

ok, so I looked at this quite a bit.  I thought maybe it was a problem with the rest of GBUI so I made it a SimpleGame to see what's going on.



There is some issue with the CanvasRootNode that i'll be digging into.


import com.jmex.bui.layout.BorderLayout;
import com.jmex.awt.applet.SimpleJMEPassApplet;
import com.jme.app.SimpleGame;
import com.jme.input.MouseInput;

public class Test /*extends SimpleGame*/ extends Mol3DJMEPassApplet
// Need to have glCanvas modifier protected for this test to work (by default, it is private in jme2)
{
  @Override
 public void simpleAppletSetup() {
      BuiSystem.init(new CanvasRootNode(glCanvas), "/rsrc/style2.bss");
      BuiSystem.getRootNode().addWindow(new RegisterWindow("Register window", BuiSystem.getStyle(), new BorderLayout()));

      getRootNode().attachChild(BuiSystem.getRootNode());

        MouseInput.get().setCursorVisible(true);
}

//    public void simpleInitGame() {
//        BuiSystem.init(new PolledRootNode(timer, input), "/rsrc/style2.bss");
//
//      BuiSystem.getRootNode().addWindow(new RegisterWindow("Register window", BuiSystem.getStyle(), new BorderLayout()));
//
//      rootNode.attachChild(BuiSystem.getRootNode());
//
//        MouseInput.get().setCursorVisible(true);
//   }
//
//    public static void main(String[] args) {
//        new Test().start();
//    }
}



if you comment out the applet portions and uncomment the simplegame portions this works just fine.

When I hover over anything with the applet it blows up, unless I turn off the mouse events in CanvasRootNode.

I'm going to have to look at this more, because I have used it many times successfully in Applets, I'm going to have to go find that code and see what's different.

The other thing I did was set some params in RegisterWindow Constructors and comment out the focusable MouseInput portion.


setLayoutManager(new BorderLayout());
MouseInput.get().setCursorVisible(true);



I do have some other info to request from you:
OS
IDE
Java version
latest JME 2 check out?
I know you're not running the latest GBUI, but that shouldn't make a difference.
standtrooper said:

I made it a SimpleGame to see what's going on.
...
if you comment out the applet portions and uncomment the simplegame portions this works just fine.

Hehe... My original post also contains a SimpleGame test (the third code portion, after RegisterWindow), I also wrote that it works fine  :D.
Edit: it just dawned on me that I should have made the title more specific... sorry about that.

OS: Windows XP
IDE: Eclipse 3.4.1 (Ganymede)
Java: JDK 1.6.0_07

JME2 Checkout: Today  ://. Obviously it wasn't so when I wrote the post (it was a couple of months old), but I don't think anything changed regarding this post after upating (double-checked before posting, everything seems to be just as before).

I really appreciate your help, by the way, thank you :).
Wizem said:

standtrooper said:

I made it a SimpleGame to see what's going on.
...
if you comment out the applet portions and uncomment the simplegame portions this works just fine.


Hehe... My original post also contains a SimpleGame test (the third code portion, after RegisterWindow), I also wrote that it works fine  :D.


doh!  I, uh, sure, wanted to see if there was anything that you had, uh, done wrong, sure that's it :D

Wizem said:

OS: Windows XP
IDE: Eclipse 3.4.1 (Ganymede)
Java: JDK 1.6.0_07

JME2 Checkout: Today  ://. Obviously it wasn't so when I wrote the post (it was a couple of months old), but I don't think anything changed regarding this post after upating (double-checked before posting, everything seems to be just as before).

I really appreciate your help, by the way, thank you :).


sure, no problem.

Alright, so I'm running on XP pro right now with IntelliJ and JDK 1.6 something...so we're ok on those fronts.

Let me trace through the CanvasRootNode some more and see what's going on.  I bet there's a resource missing on my end.  Let me double check that against the BSS and see what's going on there too.

Is there anything else that runs "funky" for you with GBUI?
standtrooper said:

Is there anything else that runs "funky" for you with GBUI?

I haven't really looked into this one other thing, I was thinking it was simple enough to fix myself and later post a small diff. But I guess it could be in on the whole funkiness thing, so here goes.

The issue is with the BScrollPane class: scrolling it with the mouse scroll results in inverted scrolling ("scroll up" scrolls down, and vice versa). This again works as expected in SimpleGame tests.

I'd started working on a simple console when I noticed this, so I posted the code below. I commented out the line that scrolls the output down automatically, because it requires a slightly modified BScrollPane class (there are no visible getValue/getMaximum methods by default)

On a side note, I guess it makes sense to be able to access these things  (getValue/getMaximum/getMinimum/etc), so perhaps it's a good idea to make them accessible in the trunk version? Sorry if that's already the case, I checked out the latest version before posting, but got the GroupLayout constraint error from the other current post, and rolled back  :// .

And on another side note, the bar doesn't scroll when being dragged, but I guess this isn't entirely unexpected; this is the same for SimpleGame :)

The test code:


import java.util.ArrayList;
import java.util.List;

import com.jme.input.KeyInput;
import com.jme.renderer.ColorRGBA;
import com.jme.system.DisplaySystem;
import com.jmex.bui.*;
import com.jmex.bui.background.BBackground;
import com.jmex.bui.background.TintedBackground;
import com.jmex.bui.event.KeyEvent;
import com.jmex.bui.event.KeyListener;
import com.jmex.bui.event.MouseEvent;
import com.jmex.bui.event.MouseListener;
import com.jmex.bui.layout.AbsoluteLayout;
import com.jmex.bui.layout.BLayoutManager;
import com.jmex.bui.layout.BorderLayout;


public class Console extends DraggableWindow
{

   public Console(String name, BStyleSheet style, BLayoutManager layout)
   {
      super(name, style, layout);
      init();
   }
   
   /** Requests focus for clicked input boxes*/
   MouseListener focusGainer = new MouseListener()
   {
      public void mouseEntered(MouseEvent event){}
      public void mouseExited(MouseEvent event){}
      public void mousePressed(MouseEvent event){}
      @Override
      public void mouseReleased(MouseEvent event)
      {
         int x0 = event.getX(), y0 = event.getY();
         for(BWindow win : BuiSystem.getRootNode().getAllWindows())
         {
            int x = win.getAbsoluteX(), y = win.getAbsoluteY();
            int w = win.getWidth(), h = win.getHeight();
            if(x0 > x && x0 < x + w && y0 > y && y0 < y + h)
            {
               win.getHitComponent(x0, y0).requestFocus();                  
               break;
            }               
         }            
      }
   };   
   
   private List<String> outputText = new ArrayList<String>();
   private StringBuilder outputTextBuilder = new StringBuilder();
   KeyListener okListener = new KeyListener()
   {
      public boolean firstTime = true;
      @Override
      public void keyPressed(KeyEvent event)
      {
         if(firstTime)
         {
            firstTime = false;            
            
            if(Character.isDefined(event.getKeyChar()))   input.setText(Character.toString(event.getKeyChar()));
            else input.setText("");
            
         }
         if (event.getKeyCode() == KeyInput.KEY_RETURN && !input.getText().equals("") && !input.getText().equals("n"))
         {            
            String inputText = input.getText();
            outputText.add(inputText);            
            input.setText("");
            outputTextBuilder.delete(0, outputTextBuilder.length());
            for(String s : outputText) outputTextBuilder.append(s + System.getProperty("line.separator"));

            output.setText(outputTextBuilder.toString());
/* This line requires a diff:*/
//            scroll.setValue(scroll.getMaximum());
            
            processText(inputText);
         }
      }
      @Override
      public void keyReleased(KeyEvent event)
      {}      
      private void processText(String text)
      {}      
   };
   final BTextField input = new BTextField("Type here");   
   final BTextArea output = new BTextArea("");
   final BScrollPane scroll = new BScrollPane(output);
//   final BSlider scroll = new BSlider(BSlider.VERTICAL, output);
   
   private void init()
   {
      BContainer componentArea = new BContainer("Input box container", new AbsoluteLayout());      
      this.add(componentArea, BorderLayout.CENTER);               
      this.add(input, BorderLayout.SOUTH);      
      input.addListener(focusGainer);
      input.addListener(okListener);      
      this.add(scroll, BorderLayout.CENTER);
      
      
      
      this.setSize(768, 190);
      this.center();
      BBackground background = new TintedBackground(new ColorRGBA(0.4f,0.3f,0.3f,0.8f));
      this.setBackground(this.getState(), background);
      BBackground inputBackground = new TintedBackground(new ColorRGBA(0.4f,0.5f,0.3f,0.8f));
      input.setBackground(input.getState(), inputBackground);
      BBackground scrollBackground = new TintedBackground(new ColorRGBA(0.4f,0.5f,0.8f,0.8f));
      scroll.setBackground(scroll.getState(), scrollBackground);
      
for(int i = 0; i < 20; i++) outputTextBuilder.append("asd " + i + System.getProperty("line.separator"));
      output.setText(outputTextBuilder.toString());

      this.setLocation(DisplaySystem.getDisplaySystem().getWidth()/2, 0);      
   }   
}




Used in an applet:


import com.jmex.awt.applet.SimpleJMEPassApplet;
import com.jmex.bui.BuiSystem;
import com.jmex.bui.CanvasRootNode;

import com.jmex.bui.layout.BorderLayout;

import gui.Console;


public class JmeGuiApplet extends SimpleJMEPassApplet
//Need to have glCanvas modifier protected for this test to work (by default, it is private in jme2)
{
   public void simpleAppletSetup()
   {      
      BuiSystem.init(new CanvasRootNode(glCanvas), "/rsrc/style2.bss");
      BuiSystem.getRootNode().addWindow(new Console("A Console", BuiSystem.getStyle(), new BorderLayout()));
      getRootNode().attachChild(BuiSystem.getRootNode());
   }
}


I'll check out the code and I'll look at the access for those variables to see what I can do to make them more publicly accessed.



On the Scrolling portion, I'm pretty sure you need to add a listener to the BScrollPane in order to scroll.  Let me look at the code real quick and verify that.



The inversion scrolling could be related to the CanvasRootNode issues as well.



Do you have a list of properties that you would like to see public getters for that are not in your list below?

Here's my diff for the BScrollPane class, it's really simple, but I guess it might be useful anyway:



Index: C:/Documents and Settings/Wizem/workspace/gbui/src/java/com/jmex/bui/BScrollPane.java
===================================================================
--- C:/Documents and Settings/Wizem/workspace/gbui/src/java/com/jmex/bui/BScrollPane.java   (revision 264)
+++ C:/Documents and Settings/Wizem/workspace/gbui/src/java/com/jmex/bui/BScrollPane.java   (working copy)
@@ -344,13 +344,48 @@
             return _hmodel == null ? 0 : -_hmodel.getValue();
         }
 
-        protected BoundedRangeModel _vmodel, _hmodel;
+        public BoundedRangeModel _vmodel, _hmodel;
         protected BComponent _target;
         protected MouseWheelListener _wheelListener;
         protected Rectangle _srect = new Rectangle();
+       
+        public void setValue(int value)
+        {
+           _vmodel.setValue(value);
+        }
+        public int getValue()
+        {
+           return _vmodel.getValue();
+        }
+        public int getMaximum()
+        {
+           return _vmodel.getMaximum();
+        }
+        public int getMinimum()
+        {           
+           return _vmodel.getMinimum();
+        }
+       
     }
 
     protected BViewport _vport;
     protected BScrollBar _vbar, _hbar;
     protected boolean _showAlways = true, _layingOut;
+   
+    public void setValue(int value)
+    {
+       _vport.setValue(value);
+    }
+    public int getValue()
+    {
+       return _vport.getValue();
+    }
+    public int getMaximum()
+    {
+       return _vport.getMaximum();
+    }
+    public int getMinimum()
+    {           
+       return _vport.getMinimum();
+    }
 }



I don't think I've run into other getters I've needed that weren't there yet; I haven't really done that much with my interface yet though :).

I'll probably be working more and more on the interface part in the closest future, especially if the issues with CanvasRootNode are resolved (it's so much more pleasant to work on something when it's functioning nicely  )... so there's a good chance I'll stumble upon something, and perhaps I'll even have some useful reusable code that I could post :).

ok, I'll get those changes made and checked in shortly.



Hopefully, I'll have an answer on the CanvasRootNode soon too

Hmm… I think the entire issue might be unimportant now :).



With the new-style applets from here: http://www.jmonkeyengine.com/jmeforum/index.php?topic=10569.15

I could use a PolledRootNode with a SimplePassGame, and all CanvasRootNode's problems were magically gone :).



I just tested with the same Console from above:



import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.util.logging.Logger;


import gui.Console;

import com.jmex.bui.BCursor;
import com.jmex.bui.BImage;
import com.jmex.bui.BRootNode;
import com.jmex.bui.BStyleSheet;
import com.jmex.bui.BuiSystem;
import com.jmex.bui.PolledRootNode;

import com.jmex.bui.layout.BorderLayout;
import com.jmex.bui.provider.DefaultResourceProvider;
import com.jmex.bui.text.BTextFactory;

import main.SimplePassApplet;

public class TestNewAppletGUI extends SimplePassApplet {

    @Override
    protected void simpleInitGame() {
   PolledRootNode root = new PolledRootNode(timer, input);
       
        initializeBuiSystem(root);
   BuiSystem.getRootNode().addWindow(new Console("A Console", BuiSystem.getStyle(), new BorderLayout()));
   rootNode.attachChild(BuiSystem.getRootNode());
   
   
    }
    private void initializeBuiSystem(BRootNode root) {
        InputStreamReader stin = null;
        try {
            stin = new InputStreamReader(ClassLoader.getSystemResourceAsStream("rsrc/style2.bss"));
            BStyleSheet stylesheet = new BStyleSheet(stin, new TestFileResourceProvider());
            BuiSystem.init(root, stylesheet);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(-1);
        } finally {
            try {
                if (stin != null) {
                    stin.close();
                }
            } catch (IOException e) {
                throw new IllegalStateException(e);
            }
        }
    }
    private class TestFileResourceProvider extends DefaultResourceProvider {
        public BTextFactory createTextFactory(String s, String s1, int i) {
            Logger.getAnonymousLogger().severe("THIS METHOD STILL USES DefaultResourceProvide!");
            return super.createTextFactory(s, s1, i);
        }

        public BImage loadImage(String s) throws IOException {
            BImage image = getImage(s);
            if (image == null) {
                return super.loadImage(s);
            }
            return image;
        }

        public BCursor loadCursor(String s) throws IOException {
            Logger.getAnonymousLogger().severe("THIS METHOD STILL USES DefaultResourceProvide!");
            return super.loadCursor(s);
        }
    }
   
    protected BImage getImage(String name) {
        try {
            return new BImage(new File(name).toURI().toURL());
        } catch (MalformedURLException e) {
            throw new IllegalStateException(e);
        } catch (IOException e) {
            throw new IllegalStateException(e);
        }
    }
}



I'll migrate my project to the new-style applets (there's actually quite a lot of difference to make up for), and I guess I'll be set with using PolledRootNode  :D

very nice…saves me some work.  Still curious as to what went wrong with the CanvasRootNode, but I like not having to mess with it and the option to eventually get rid of it