[Solved] How to add scroll bar to Container in Lemur?

Hi
How can I add a scroll bar to my simple file explorer in lemur?
it consists of a main Container with Labels and nested RollupPanels.

like this :

Thanks in advance

JME doesn’t let me clip so there is no easy general way to do this at this point.

One approach you can take in this cases is to put your panel in a viewport and place a scroll bar next to it to scroll the position of the thing in the viewport. If you are unfamiliar with managing viewports, I might be able to provide an example app state for managing one. It won’t have the pixel positioning and stuff you’ll need but that’s just math.

If you don’t have multiple overlapping scrollable panels in your main UI then viewport is probably still the easiest way to get this working until JME lets clipping happen. (I’ve already looked into it and it’s complicated but high on my to-do list for 3.2.)

Thanks Paul
Yes as you said I used positioning.
Implemented a MouseAxisTrigger listener and changed position of container with mouse scroll Up/Down.

Also will implement a visual scroll bar as you said.

Yes please. Will appreciate you.
Thanks for Lemur. So Powerful and Flexible.

That looks nice… you may not even need a viewport in that situation.

So, I created my own scroll bar using lemur and the idea of a separate viewport as stated above. Everything works as expected (scroll bar scrolls, contents from the scroll bar scrolls, etc…). However, the one problem I have is with picking components inside the contents, which is in the other viewport. Here’s a rough piece of code showing how I setup my viewport and the contents that I place in it.

viewport.setClearFlags(false, false, false);
GuiGlobals.getInstance().setupGuiComparators(viewport);
float width = getSize().x - outerBar.getSize().x;
float height = getSize().y;
float x1 = getWorldTranslation().x / width;
float x2 = (width + getWorldTranslation().x) / width;
float y1 = (getWorldTranslation().y - height) / height;
float y2 = (getWorldTranslation().y) / height;
viewport.getCamera().resize((int) width, (int) height, true);
viewport.getCamera().setViewPort(x1, x2, y1, y2);
contents.setQueueBucket(RenderQueue.Bucket.Gui);
contents.setCullHint(CullHint.Never);
app.getState(MouseAppState.class).addCollisionRoot(contents, viewport, PickState.PICK_LAYER_GUI);

With this setup, my contents display correctly (with clipping), but the picking of a component in the contents (e.g. an ActionButton) is offset. In other words, if I click on the component, nothing happens. But if I click a bit over to the side (my mouse no longer on the button), then the button triggers. Its as if the picking doesn’t account for the fact that I resized the viewport. If I don’t resize the viewport (remove the viewport.getCamera().setViewPort(x1, x2, y1, y2);) then picking works as it should.

Any idea what’s going on here?

The gui bucket is very strange and tends to avoid all regular logic that one would expect to work. I suspect this is yet another case where the gui bucket is having its way with our nether regions.

Someone posted a similar problem and solution but the solution looked like it would break other things. This is very specific to a constrained viewport and the Gui bucket and any solution should be specific to that case.

Where you want to look is in the method on PickEventSession that gets the pick ray. (It might be called getPickRay() but I’m going from memory.) In your use-case, it is returning the wrong ray. It probably needs to look a little closer and determine how to properly clip the screen coordinate.

Ah OK I see. I found the method getPickRay() that you mentioned and I’m currently looking into what would be the best solution in order to get a correct Ray. Thanks for pointing me in the right direction.

As I recall, there is a specific branch in there that deals with the Gui bucket. Probably just need to look and see if the viewport has been constrained and adjust accordingly.

Also, when I setup my own constrained viewport, I don’t remember resizing the camera. I’m not sure that’s necessary.

I was successful in adjusting the Ray for picking. This is the code that I modified; I’ve tested with multiple camera and viewport values and it worked for every one.

    if( rootEntry.root instanceof Spatial && ((Spatial)rootEntry.root).getQueueBucket() == Bucket.Gui ) {
        trace("Creating GuiBucket ray.");
        // Special case for Gui Bucket nodes since they are always in screen space
        float x1 = cam.getViewPortLeft();
        float x2 = cam.getViewPortRight();
        float y1 = cam.getViewPortBottom();
        float y2 = cam.getViewPortTop();
        float cursorX = (cursor.x / cam.getWidth() - x1) / (x2 - x1) * cam.getWidth();
        float cursorY = (cursor.y / cam.getHeight() - y1) / (y2 - y1) * cam.getHeight();
        result = new Ray(new Vector3f(cursorX, cursorY, 1000), new Vector3f(0, 0, -1));
    } else {
        ...
    }

Let me know if you would like me to submit a PR.

Well you may be right, but it seems if I don’t resize it, my content no longer displays correctly. Since it works with the resizing, I rather not try to fix something that doesn’t need fixing (for now) :stuck_out_tongue: . Thanks for pointing that out tho.

1 Like

The thing is that all of this stuff may be related.

I guess I’ll have to do some testing. The way I thought it worked was that given a camera unresized, 100, 100 is going to be the same place on the screen no matter what your set your viewport to.

Then again, that seems entirely logical and I remember nothing about viewports being logical. So who knows?

Don’t know if you still needing help with this but the viewport panels I posted on here are working fine. Maybe is one of the “but the solution looked like it would break other things” pspeed mentioned but for the moment it isn’t working anything ;). You can find the link to the code code on the given link.