[LibGDX] Trouble with multiple table layouts

I’m attempting to render a GUI in LibGDX on top of my game. I have a container table, and then two separate tables that I would like to render. One goes in the top left, the other in the bottom left. However, they will only render in the middle and I’m not sure what I’m doing wrong.


stage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), true);
stage.setCamera(controller.getCamera());
		
uiSkin = new Skin(Gdx.files.internal("res/gui/skin/uiskin.json"));
		
Table container = new Table(uiSkin);
Table infoTable = new Table(uiSkin);
Table chatTable = new Table(uiSkin);
container.debug();
infoTable.debug();
chatTable.debug();
		
container.setFillParent(true);
		
// Add the tables to the container table
container.add(chatTable).bottom().left().pad(10);
container.row();
container.add(infoTable).top().left().pad(10);
stage.addActor(container);
		
// Set up our chat table
chatTable.setFillParent(false);
chatTable.setWidth(300);
chatTable.setHeight(400);
		
// Set up the information Table
infoTable.setFillParent(false);
infoTable.setWidth(300);
infoTable.setHeight(500);
		
lblChatLabel = new Label("", uiSkin);
lblChatLabel.setWrap(true);
		
// Information Table Actors
lblPilotName = new Label(pilotName, uiSkin);
lblLocationInfo = new Label(locationInfo, uiSkin);
lblCreditsOnShip = new Label(creditsOnShip, uiSkin);
lblCreditsInBank = new Label(creditsInBank, uiSkin);
		
final ScrollPane scroll = new ScrollPane(lblChatLabel, uiSkin);
		
txtChatBar = new TextField("do a chat", uiSkin);
txtChatBar.setName("txtChatBar");
		
// Populate the Chat Table
chatTable.add(txtChatBar).expandX();
chatTable.row();
chatTable.add(scroll).expand().fill().colspan(4);
		
// Populate the Information Table
infoTable.add(lblPilotName);
infoTable.row();
infoTable.add(lblLocationInfo);
infoTable.row();
infoTable.add(lblCreditsOnShip);
infoTable.row();
infoTable.add(lblCreditsInBank);

And here a picture of the result. Even though I declared the width to be 300, the debug lines show that the table’s width is certainly not 300. Also, within the container there only appears to be one table, not two (infoTable and chatTable). Any ideas are much appreciated!

Just try:


table.setPosition();


Since you are wanting it at the edges of the screen.

Figuring out how to position gui elements is always a struggle for me… Here’s how I managed to place 3 buttons in 3 corners of the screen:

guiStage = new Stage(Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), false);
guiRoot = new Table();
guiRoot.setFillParent(true);
guiStage.addActor(guiRoot);

guiRoot.add(menuButton).width(BUTTON_SIZE).height(BUTTON_SIZE).expand().top().left();

guiRoot.add(redirectButton).width(BUTTON_SIZE).height(BUTTON_SIZE).top().right().row();

guiRoot.add(starBaseButton).width(BUTTON_SIZE).height(BUTTON_SIZE).expand().bottom().left();

The trick may be the “expand” option. It doesnt make my buttons fill the screen, but somehow it does help in placing them correctly.

expand() worked! Thanks for that suggestion!

Unfortunately, table.setPosition() places it in an absolute position and my camera moves to follow the spaceship. So the GUI elements then move off screen. I suppose I could call setPosition() every frame, but it’s easier to let the stage handle all that for me.

I do have one further question. The chat table uses a ScrollPane with a Label to display chat text. Unfortunately, this only shows a big box with no scroll bar. It won’t display any text in the Label either. Relevant code below:


lblChatLabel = new Label("", uiSkin);
lblChatLabel.setWrap(true);

final ScrollPane scroll = new ScrollPane(lblChatLabel, uiSkin);

txtChatBar = new TextField("do a chat", uiSkin);
txtChatBar.setName("txtChatBar");

// Populate the Chat Table
chatTable.add(txtChatBar).width(300).expand();
chatTable.row();
chatTable.add(scroll).width(300).height(200).expand();

txtChatBar.addListener(new InputListener() {
	public boolean keyDown(InputEvent event, int keycode) {
		if (keycode == Input.Keys.ENTER) {
			sendMessage();
			// Close the chat bar
			showChat = false;
			return true;
		}
		
		return false;
	}
});

So the Label for the ScrollPane is initialized with an empty String. When the user passes input, the InputListener calls sendMessage().


private void sendMessage() {
	String message = txtChatBar.getText().toString();
	// Hand input control back to the Game Controller
	controller.getVoidGame().unloadInputProcessor(stage);
	chatText += message + "\n";
	lblChatLabel.setText(chatText);
}

Even after adding multiple messages to the Label (I can confirm they are added by printing the lblChatLabel.getText()), nothing is displayed in the ScrollPane. Any suggestions on this issue? Thanks!

BTW, new image:

Ok, thats another thing I struggled with as well. This is what I did to get the scrollpane working:

		ScrollPane scrollPane = new ScrollPane(missionList, skin);
		scrollPane.setForceScroll(false, true);
		scrollPane.setFadeScrollBars(false);
		Table container = new Table();
		container.add(scrollPane).fill().expand();
		add(container).colspan(2).fill().expand().row();

		scrollPane.layout();

Maybe you need to call layout() on the scrollPane after adding things to the label (without layout() the scrollbar or scrollpane didnt show for me). Some other things to try:

  • only call expand() on scroll, not on txtChatBar
  • try setting the label to a fixed size

Hmm, I’m still not able to get it. I can see the scroll bar now since I’ve setForceScroll to true, but the text in the Label disappears almost immediately. To test, I added a really long multi-line string as the lblChatLabel text. When the program starts, you can see it flash in the scroll pane, but it quickly disappears. The scroll bar also reflects the long text, but clicking and dragging or otherwise trying to interact with the scroll bar does nothing (even after forcing the stage to be the only input listener).

Super frustrating. This should be a simple thing.

I’m guessing the issue is now something with my camera or the way I’m rendering elsewhere. I just created a brand new project and only used the GUI code, and everything displays and works as expected.

Okay, one more update. I think I’ve finally narrowed it down. During rendering, I have the following code:


float cx = controller.getCamera().getX();
float cy = controller.getCamera().getY();
float width = controller.getCamera().viewportWidth;
float height = controller.getCamera().viewportHeight;
		
stage.act();
stage.setViewport(width, height, true, cx, cy, width, height);
stage.draw();
Table.drawDebug(stage);

Everything renders, but the ScrollPane does not work properly and the Label text is not displayed inside. However, everything is rendered in the correct location.

If I comment out the stage.setViewport line, everything renders correctly (even the ScrollPane and Label!), but it does not translate to the camera position. The GUI renders at 0,0. I can fly the space ship to 0,0 and everything is there and in the correct position.

So my question is now this: is this a possible bug? Here is another screenshot to help illustrate after I comment out the setViewport and I have moved near 0,0.

Okay, here is my last post on this subject, since I finally figured out how to get around the issue! The solution is rather simple. Rather than setting the viewport, I decided to update the position of each of the actors.


for (Actor a : stage.getActors()) {
	a.setPosition(controller.getCamera().position.x, controller.getCamera().position.y);
}
stage.act();
//stage.setViewport(width, height, false, cx, cy, width, height);
stage.draw();
Table.drawDebug(stage);

With that, everything in the GUI moves properly as the camera moves. Hope this helps someone else in the future.