how i create a scroll panel like this image with lemur
For a scroll panel that just contains arbitrary stuff then you will need to manage a viewport, etc… I think someone has already done something like this in another thread.
Lemur doesn’t (yet) have this functionality. JME doesn’t provide an easy way to do this and viewports come with a bunch of caveats that make a general solution tricky.
i can´t put work
this is my code
package mygame;
import com.jme3.app.Application;
import com.jme3.app.state.BaseAppState;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.simsilica.lemur.ActionButton;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.CallMethodAction;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.FillMode;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.PasswordField;
import com.simsilica.lemur.TabbedPanel;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.RollupPanel;
/**
*
* @author Pedro Alves
*/
public class OptionsState extends BaseAppState {
private Container loginPanel;
private TextField nameField;
private PasswordField passwordfield;
public OptionsState() {
}
@Override
protected void initialize(Application app) {
loginPanel = new Container();
loginPanel.addChild(new Label("Options", new ElementId("title")));
Container props = loginPanel.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X, FillMode.None, FillMode.Last)));
props.setBackground(null);
TabbedPanel tabs = new TabbedPanel();
Container panel1 = tabs.addTab("Game Options", new Container());
panel1.addChild(new Label("Game Options"));
panel1.addChild(new RollupPanel("fsdfdsf",new ElementId("ds"),"grass"));
Container panel2 = tabs.addTab("Graphic Options", new Container());
panel2.addChild(new Label("Grapicos options"));
Container panel3 = tabs.addTab(" Audio Options", new Container());
panel3.addChild(new Label("Audio"));
Container panel4 = tabs.addTab("Key Mapping ", new Container());
panel4.addChild(new Label("Key Mapping"));
loginPanel.addChild(tabs);
/* props.addChild(new Label("Username:"));
nameField = props.addChild(new TextField(System.getProperty("user.name")), 1);
props.addChild(new Label("Password:"));
passwordfield=props.addChild(new PasswordField(System.getProperty("Pass.word")), 1);
Container buttons = loginPanel.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
*/ // buttons.setBackground(null);
//buttons.setLayout(new SpringGridLayout(Axis.X, Axis.Y));
// buttons.addChild(new ActionButton(new CallMethodAction("Join", this, "join")));
// buttons.addChild(new ActionButton(new CallMethodAction("Cancel", this, "cancel")));
float scale = 1.5f * getState(MainMenuState.class).getStandardScale();
loginPanel.setLocalScale(scale);
Vector3f prefs = loginPanel.getPreferredSize().clone();
prefs.x = Math.max(300, prefs.x);
loginPanel.setPreferredSize(prefs.clone());
// Now account for scaling
prefs.multLocal(scale);
int width = app.getCamera().getWidth();
int height = app.getCamera().getHeight();
loginPanel.setLocalTranslation(width * 0.5f - prefs.x * 0.5f, height * 0.5f + prefs.y * 0.5f, 10);
}
@Override
protected void cleanup(Application app) {
}
@Override
protected void onEnable() {
Node root = ((Main)getApplication()).getGuiNode();
root.attachChild(loginPanel);
}
@Override
protected void onDisable() {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
loginPanel.removeFromParent();
}
}
Because I can’t think of too many different ways to say this today, I’m just going to repeat myself.
Can’t put the viewport work
This is my code
protected void initialize(Application app) {
loginPanel = new Container();
loginPanel.addChild(new Label(“Options”, new ElementId(“title”)));
Container props = loginPanel.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X, FillMode.None, FillMode.Last)));
props.setBackground(null);
TabbedPanel tabs = new TabbedPanel();
Panel panel1=new Panel(500,2000);
ViewportPanel panel= new ViewportPanel(panel1.getElementId(),panel1.getStyle());
Container tab1 = tabs.addTab(“Game Options”, new Container());
tab1.addChild(new Label(“Game Options”));
panel.attachScene(panel1);
tab1.addChild(panel1);
Container tab2 = tabs.addTab(“Graphic Options”, new Container());
tab2.addChild(new Label(“Grapicos options”));
Container tab3 = tabs.addTab(" Audio Options", new Container());
tab3.addChild(new Label(“Audio”));
Container tab4 = tabs.addTab("Key Mapping ", new Container());
tab4.addChild(new Label(“Key Mapping”));
loginPanel.addChild(tabs);
/* props.addChild(new Label("Username:"));
nameField = props.addChild(new TextField(System.getProperty("user.name")), 1);
props.addChild(new Label("Password:"));
passwordfield=props.addChild(new PasswordField(System.getProperty("Pass.word")), 1);
*/ Container buttons = loginPanel.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
buttons.setBackground(null);
buttons.setLayout(new SpringGridLayout(Axis.X, Axis.Y));
buttons.addChild(new ActionButton(new CallMethodAction("Default", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("Reset", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("Apply", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("OK", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("Cancel", this, "cancel")));
float scale = 1.5f * getState(MainMenuState.class).getStandardScale();
loginPanel.setLocalScale(scale);
Vector3f prefs = loginPanel.getPreferredSize().clone();
prefs.x = Math.max(300, prefs.x);
loginPanel.setPreferredSize(prefs.clone());
// Now account for scaling
prefs.multLocal(scale);
int width = app.getCamera().getWidth();
int height = app.getCamera().getHeight();
loginPanel.setLocalTranslation(width * 0.5f - prefs.x * 0.5f, height * 0.5f + prefs.y * 0.5f, 10);
}
A custom scroll view of something where you know what the list is can be done without a view port. I hacked a “hello world” example. You manage the sublist that is visible manually. The visual state is just this sublist. Now you only need to manage a fixed number of items with a forward/back button.
I think this would be easier than viewports.
I did the same thing with a array of items that don’t fit.
Attach the viewport panel (pointed by your var “panel”) to any other container. You are currently creating that panel but doing nothing with it.
I always thought about porting this back from my own ui back then, but did not find time(and need) to do so.
Basically I used a own material (e.g replace all lemur default ones for this) that contains a cliprectangle as an shader parameter. Everything outside those is then getting discarded in the fragment shader. This way a proper scroll is implementable without having to use a secondary viewport. Scrolltables would be required to set the cliprectange for all children recursivly if the child is not a scrollpane itself. Also they need to merge a cliprectangle from the parent. Further optimisation is to simply flip the cullhint, if a objects cliprectangle is 0 in any direction.
Isn’t that too much work for something that can be easily done with a viewport?.
What about efficiency?, is any of the methods noticeable more/less efficient than the other? (I don’t really know how a new viewport can impact in the overall performance)
Well using a cliprectangle is extremly fast, because all extra work (appart from setting and updating it) is exclusivly done by the gpu. It further allows other similar features, like a button to cutoff text, if it does not fit without resorting to manually manipulating strings.
However I think a viewport will not be noticeably slower, after all we talk about like 1-3 Panels max at a given ui.
Theoretically could a Viewport also render with slower framerate than the main window.
How exactly is the work to make event handling properly work trough a viewport is however outside of my knowledge, but it might work just out of the box with the system pspeed provides.
I actually do not care much about the technical way this is done, after all if it has a clean interface it could be easily replaced later on if problems appear, no matter what approach was choosen. I’m more interested in a working Scrolllpanel that just works out of the box
The viewportpanel I shared manages the pspeed’s input correctly. Which is the one is using OP (or that I assume after receiving some PM from him… I never verified it O.o).
I use the 2D version to create a scroll panel too:
However, it scrolls with the mouse or the arrows (not bar). It’s as easy as translate it up/down when desired. It should be easy to make a generic scrollpanel to use in most common cases (with toggleable horizontal/vertical bars) but didn’t have the time yet.
I did a profile:
No ViewportPanel :
Four empty ViewportPanel :
And four ViewportPanel with high poly child (~ 12K face) attached
Edit:
And one ViewportPanel with four child:
As I said it is probably not that much.
However might I ask, why you render that items with a Viewport? Do they change frequently? or is it just to get a Model there?
I use a system for inventorys, where I generate a image via a secondary viewport only once, and then reuse that image.
(Of course that would not be useable for scrolls, but for a character select it might be an alternative option)
I am using a Viewport because :
Yes I want a mode there (and I am playing tween animation on them).
Which will participate on drag & drop stuff on scene.
But it is not a must to put 3D models in there, of course I can just put an icon instead, and I will do if I run to performance issues.
can i put a example
when i try put
tab1.addChild(panel);
give this error
Uncaught exception thrown in Thread[jME3 Main,5,main]
NullPointerException
You’ve left out all of the important parts of the exception.
Don’t bother posting exceptions without a track trace. They aren’t useful.
i fix the error but i have this result
how put a scroll bar in my panel
package mygame;
import com.jme3.app.Application;
import com.jme3.app.state.BaseAppState;
import com.jme3.math.Vector3f;
import com.jme3.renderer.ViewPort;
import com.jme3.scene.Node;
import com.simsilica.lemur.ActionButton;
import com.simsilica.lemur.Axis;
import com.simsilica.lemur.CallMethodAction;
import com.simsilica.lemur.Container;
import com.simsilica.lemur.FillMode;
import com.simsilica.lemur.Insets3f;
import com.simsilica.lemur.Label;
import com.simsilica.lemur.Panel;
import com.simsilica.lemur.PasswordField;
import com.simsilica.lemur.TabbedPanel;
import com.simsilica.lemur.TextField;
import com.simsilica.lemur.component.SpringGridLayout;
import com.simsilica.lemur.style.ElementId;
import com.simsilica.lemur.RollupPanel;
import mygame.panels.ViewportPanel;
/**
*
* @author Pedro Alves
*/
public class OptionsState extends BaseAppState {
private Container loginPanel;
private TextField nameField;
private PasswordField passwordfield;
private ViewPort viewPort;
protected void apply() {
// String name = nameField.getText().trim();
// String password= passwordfield.getText().trim();
// if( getState(ConnectionState.class).joinserver(nameField.getText(),passwordfield.getText()) ) {
// getStateManager().detach(this);
//}
}
protected void join() {
// String name = nameField.getText().trim();
// String password= passwordfield.getText().trim();
// if( getState(ConnectionState.class).joinserver(nameField.getText(),passwordfield.getText()) ) {
// getStateManager().detach(this);
//}
}
protected void cancel() {
getStateManager().attach(new MainMenuState());
getStateManager().detach(this);
// String name = nameField.getText().trim();
// String password= passwordfield.getText().trim();
// if( getState(ConnectionState.class).joinserver(nameField.getText(),passwordfield.getText()) ) {
// getStateManager().detach(this);
//}
}
public OptionsState() {
}
@Override
protected void initialize(Application app) {
loginPanel = new Container();
loginPanel.addChild(new Label("Options", new ElementId("title")));
Container props = loginPanel.addChild(new Container(new SpringGridLayout(Axis.Y, Axis.X, FillMode.None, FillMode.Last)));
props.setBackground(null);
TabbedPanel tabs = new TabbedPanel();
Panel panel1=new Panel(500,200);
panel1.setInsets(new Insets3f(10, 10, 10, 10));
ViewportPanel panel= new ViewportPanel(getStateManager(),panel1.getElementId(),panel1.getStyle());
panel.attachScene(panel1);
Container tab1 = tabs.addTab("Game Options", new Container());
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
panel1=tab1.addChild(new Label("Game Options"));
tab1.addChild(panel1);
tab1.addChild(panel);
Container tab2 = tabs.addTab("Graphic Options", new Container());
tab2.addChild(new Label("Grapicos options"));
Container tab3 = tabs.addTab(" Audio Options", new Container());
tab3.addChild(new Label("Audio"));
Container tab4 = tabs.addTab("Key Mapping ", new Container());
tab4.addChild(new Label("Key Mapping"));
loginPanel.addChild(tabs);
Container buttons = loginPanel.addChild(new Container(new SpringGridLayout(Axis.X, Axis.Y)));
buttons.setBackground(null);
buttons.setLayout(new SpringGridLayout(Axis.X, Axis.Y));
buttons.addChild(new ActionButton(new CallMethodAction("Default", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("Reset", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("Apply", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("OK", this, "join")));
buttons.addChild(new ActionButton(new CallMethodAction("Cancel", this, "cancel")));
float scale = 1.5f * getState(MainMenuState.class).getStandardScale();
loginPanel.setLocalScale(scale);
Vector3f prefs = loginPanel.getPreferredSize().clone();
prefs.x = Math.max(300, prefs.x);
loginPanel.setPreferredSize(prefs.clone());
// Now account for scaling
prefs.multLocal(scale);
int width = app.getCamera().getWidth();
int height = app.getCamera().getHeight();
loginPanel.setLocalTranslation(width * 0.5f - prefs.x * 0.5f, height * 0.5f + prefs.y * 0.5f, 10);
}
@Override
protected void cleanup(Application app) {
}
@Override
protected void onEnable() {
Node root = ((Main)getApplication()).getGuiNode();
root.attachChild(loginPanel);
}
@Override
protected void onDisable() {
//throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
loginPanel.removeFromParent();
}
}
can you put the code with viewportpanel
Link to ViewportPanel is available here
i want know how you put the view port panel to work
i can´t make it work