Nifty ListBox scrollbar position [SOLVED]

Hey all…



I have some trouble in keeping the scroll position on a listbox element that has a vertical scroll bar.

I think is the same problem described here https://github.com/void256/nifty-gui/issues/52 and fixed in nifty 1.3.2



Is there any way to read the visible objects from the listbox so I can focus the list back on?



Thanks in advance!

There is no easy way to read the visible objects I think. I’m not saying it’s impossible tho :>



BUT can you describe the problem a bit more in detail? If it’s a bug or a missing feature I’d like to fix this! :slight_smile:

1 Like

Thanks for the answer @void256 :slight_smile:

Here’s a little testcase:



[java]public class TestNiftyGui extends SimpleApplication implements ScreenController {



private Nifty nifty;

private ListBox myList;

private Random rand = new Random();

private float time = 0f;



public static void main(String[] args) {

TestNiftyGui app = new TestNiftyGui();

app.start();

}



public void simpleInitApp() {

NiftyJmeDisplay niftyDisplay = new NiftyJmeDisplay(assetManager,

inputManager,

audioRenderer,

guiViewPort);

nifty = niftyDisplay.getNifty();

nifty.fromXml(“Test/HelloJme.xml”, “start”, this);

guiViewPort.addProcessor(niftyDisplay);

flyCam.setEnabled(false);

inputManager.setCursorVisible(true);



myList = nifty.getCurrentScreen().findNiftyControl(“list”, ListBox.class);

for (int i = 0; i < 30; i++) {

myList.addItem(rand.nextInt(999999999) + 1000000000);

}

myList.sortAllItems();

myList.refresh();

}



@Override

public void simpleUpdate(float tpf) {

if (time > .5f) {

myList.removeItemByIndex(rand.nextInt(myList.itemCount()));

myList.addItem(rand.nextInt(999999999) + 1000000000);

myList.sortAllItems();

myList.refresh();

time = 0f;

} else {

time = time + tpf;

}

}



public void bind(Nifty nifty, Screen screen) {

}



public void onStartScreen() {

}



public void onEndScreen() {

throw new UnsupportedOperationException(“Not supported yet.”);

}

}[/java]



[xml]<nifty>

<useControls filename=“nifty-default-controls.xml” />

<useStyles filename=“nifty-default-styles.xml” />

<screen id=“start” controller=“jme3test.niftygui.TestNiftyGui”>

<layer id=“layer” backgroundColor="#0000" childLayout=“center”>

<panel id=“panel” height=“400” width=“200” align=“center” valign=“center” childLayout=“center” visibleToMouse=“true”>

<control id=“list” name=“listBox” selectionMode=“Single” vertical=“optional” horizontal=“off” displayItems=“20” />

</panel>

</layer>

</screen>

</nifty>[/xml]



The number of elements is fixed, it randomly deletes an element and adds a new one every half sec.

If you run it, you will notice is very hard to select the last element, because the scroll bar jumps up one position whenever a new element is added…

1 Like

Thanks for the testcase! I can understand the problem now. Unfortunately I don’t have an idea how to solve that automatically :confused:



Here is what is happening:


  1. Let’s assume you’ve scrolled down to the end of the list. The last item is visible.
  2. Calling removeItemByIndex() will remove some element somewhere in the list (not necessarily an element that’s currently visible).
  3. Now the list contains one element less and Nifty will update the Scrollbar with the total number of elements in the list so that the Scrollbar can correctly resize the scrollhandle. Until now everything is fine.
  4. Calling addItem() will add an item somewhere before the last element (or actually before all elements we can currently see). Adding the element however will push the currently visible elements down one element. Which in turn makes the last element disappear.



    I think most text editors with scrollbars work the same. If you scroll so that the last line of the text is visible at the bottom and then you insert new lines of text somewhere above these last lines the end of the document will disappear.



    If for some reason you want to force the last item into view you can do that with the showItem() or showItemByIndex() methods after you’ve modified the list. But as you’ve noticed there currently is no public way to access the visible elements so that you could work around this in a more general way. Something like: remember which element is visible before you remove/add items and make sure it is visible when you’re done with your changes using showItem(). Internally this information is available (viewOffset in the ListBoxImpl class) but it’s not public accessible right now. I’m not sure if this problem justifies making this information a part of the public ListBox API considering it might just be used for this workaround :confused:



    I guess what you would expect is that the last item is still visible after your changes since the total count of elements has (in total) not changed.



    Not sure how to add that. Any ideas? :slight_smile:

I’d suggest that when adding an item to the list nifty remembers the last currently visible item, and then after adding it makes sure that item is still the last currently visible one.

…to complete a little bit @zarch 's idea, that could be done in the refresh method, after you remember the last visible index in add method.

Another suggestion - the add method not to change the float vertical position of the scroll bar; or refresh to reset it to the value before last add method?

Why do it in the refresh method at all? Just do it as part of the add process. Remember Position, Add Item, Check Position and Adjust…all one method.



There is an argument as well for saying that if you are viewing the last item in the list and a new one adds on the end then you should scroll down to view that one too.

1 Like

@zarch:

That is a valid argument but you’ve convinced me that saving the visible elements before the operation and restoring it after the operation is the better approach. In fact I’ve already added it and it actually looks so much more reasonable :smiley: Here is the related commit (Nifty 1.4) https://github.com/void256/nifty-gui/commit/1dbeb7bfb7e40790f464b1e474dbf6493c24fea5 Thanks for the suggestion to both of you!



If one adds an element to the end of the list and wants that this element is visible one can simply do that manually. In fact the controls demo already did that with something like:



[java] // add the item and make sure that the last item is shown

listBox.addItem(new JustAnExampleModelClass(addTextField.getText()));

listBox.showItemByIndex(listBox.itemCount() - 1);[/java]



I think this is a reasonable approach for this special case.

3 Likes

@void256 & @zarch thanks guys!

1 Like