ListBox - relu91/nifty-gui GitHub Wiki
The ListBox control displays multiple items (usually Text but this can be changed) and allows the selection of a single, multiple or none of the items. The selection can be retrieved from Java and items can be added and removed. The amout of lines the ListBox displays can be configured. Additionally it supports the display of horizontal and/or vertical scrollbars. This is necessary if the ListBox contains more items than being display.
ListBox creation is dynamically possible from Java with the ListBoxBuilder class as well as from XML using Niftys Standard <control></control> Tag. Please note that you can't populate the ListBox from XML anymore. Adding items must be done from Java.
The Java API is based on storing your objects directly in the ListBox. So you can take for instance your "Sword" class ;-) and store it in the ListBox. The default behaviour is the usage of the toString() method to determine which information the ListBox displays for a certain instance it manages. This however can be changed with the ListBoxViewConverter Interface. This is not necessary if your Object can (or already does) implement the toString() method.
The following table describes all ListBox parameters.
Name | Datatype | Default | Description |
displayItems | integer | 2 | Defines the number of Items this ListBox should show without scrolling. This should me greater than 2 because the Scrollbar handles are currently too big to be displayed correctly for displayItems = 1 :) |
selectionMode | one of: "Single", "Multiple", "Disabled" (Attention: this is case sensitive!) | "Single" | The selection mode this ListBox should use. You can choose between: "Single" - which only allows a single item to be selected, "Multiple" - which allows multiple items to be selected, "Disabled" - which does not allow selection at all |
forceSelection | boolean | false | When set to "true" the ListBox will not allow to deselect the selection. There always needs to be a selection. When set to "false" the selection can be empty. |
vertical | one of: "off", "on", "optional" (Attention: this is case sensitive!) | "on" | Display a vertical scrollbar next to the ListBox. You can always show the scrollbar (vertical="on") or you can always switch it off (vertical="off"). There is now even the possibility to only show the scrollbar when necessary (vertical="optional"). |
horizontal | one of: "off", "on", "optional" (Attention: this is case sensitive!) | "on" | Display a horizontal scrollbar below the ListBox. You can always show the scrollbar (horizontal="on") or you can always switch it off (horizontal="off"). There is now even the possibility to only show the scrollbar when necessary (horizontal="optional"). |
viewConverterClass | Full qualified class name of the ListBoxViewConverter class to use (must implement the ListBoxViewConverter) | ListBoxViewConverterSimple | You can specify the class that should be used when converting items for the Listbox. When ommited a default is used that simply uses .toString() of all the managed objects. |
The ListBox supports EventBus notification for any action that changes the selection of the ListBox. In that case an instance of ListBoxSelectionChangedEvent is published using the id of the ListBox as the Topic for the Event.
Create a ListBox control that displays 4 items, allows selection of multiple items and does only show scrollbars when necessary.
// Using the builder pattern
control(new ListBoxBuilder("myListBox") {{
displayItems(4);
selectionModeMutliple();
optionalHorizontalScrollbar();
optionalVerticalScrollbar();
width("*"); // standard nifty width attribute
}});
Create a ListBox control that displays 4 items, allows selection of multiple items and does only show scrollbars when necessary.
<!-- using XML -->
<control id="myListBox" name="listBox" vertical="optional" horizontal="optional" displayItems="4" selection="Multiple" />
It does not matter how you create the ListBox (with the Java Builder or with XML, see above). You can use it like this:
public class ListBoxTestScreenController implements ScreenController {
...
/**
* Fill the listbox with items. In this case with Strings.
*/
public void fillMyListBox() {
ListBox listBox = screen.findNiftyControl("myListBox", ListBox.class);
listBox.addItem("a");
listBox.addItem("b");
listBox.addItem("c");
}
/**
* When the selection of the ListBox changes this method is called.
*/
@NiftyEventSubscriber(id="myListBox")
public void onMyListBoxSelectionChanged(final String id, final ListBoxSelectionChangedEvent<String> event) {
List<String> selection = event.getSelection();
for (String selectedItem : selection) {
System.out.println("listbox selection [" + selectedItem + "]");
}
}
}
You can easily add you own objects to the ListBox. To make that work you only need to support the .toString() method so that the ListBox knows what it should display.
/**
* This is just an example. This should really be your own model. The ListBox does call toString() on this
* to get the label it should display. But you can use your own ListBoxViewConverter if you want to use more
* sophisticated mechanism.
* @author void
*/
public class JustAnExampleModelClass {
private String label;
public JustAnExampleModelClass(final String label) {
this.label = label;
}
public String toString() {
return label; // we could really use anything in here, the name of the sword or something ;-)
}
}
...
/**
* Fill the listbox with items. In this case with JustAnExampleModelClass.
*/
public void fillMyListBox() {
ListBox<JustAnExampleModelClass> listBox = (ListBox<JustAnExampleModelClass>) screen.findNiftyControl("myListBox", ListBox.class);
listBox.addItem(new JustAnExampleModelClass("You can add more lines to this ListBox."));
listBox.addItem(new JustAnExampleModelClass("Use the append button to do this."));
}
...
@NiftyEventSubscriber(id="myListBox")
public void onListBoxSelectionChanged(final String id, final ListBoxSelectionChangedEvent<JustAnExampleModelClass> event) {
List<JustAnExampleModelClass> selection = event.getSelection();
// do something with the selection
}
The ListBox is not only capable of displaying simple texts. It can display any (!) element or any combination of elements that Nifty supports. It is not limited to the display of text elements at all! You can even use controls, for instance to make a ListBox full of Buttons (or a ListBox that consists of other ListBoxes ... uhm, but I admit that I've not yet tried that ;-)
In this example we'll take a look at how this is done in the chat control. The chat control uses a ListBox internally to display the chat messages. Each chat message consists of an image (for the avatar image) and the actual text (for the chat message).
Here is an example screenshot from the ListBox that is used in the ChatControl:
We will use a control to combine an image and a text element together. In XML that controlDefinition would look something like that (btw: you could use the JavaBuilder in the same way):
<!-- this defines the control for a single line of text in the chat and player window -->
<controlDefinition name="nifty-chat-line">
<panel childLayout="horizontal" width="100%" align="center">
<image id="#chat-line-icon" width="23px" height="23px" />
<control id="#chat-line-text" name="label" align="left" textHAlign="left" height="23px" width="*" wrap="true" />
</panel>
</controlDefinition>
So this is a simple control that consists of a single panel that aligns its two child elements, an image and a label, using a horizontal childLayout. (Again, you could really use anything in here ...)
So the next step is to tell the ListBox to display our control instead of its default text element. To do this we can nest the control inside of the ListBox control (make it a child element). Again, we show it using XML but it works the same using the JavaBuilder:
<control id="myCustomListBox" name="listBox" vertical="optional" horizontal="off" selection="Disabled" displayItems="15"
viewConverterClass="de.lessvoid.nifty.controls.chatcontrol.ChatBoxViewConverter">
<control name="nifty-chat-line" controller="de.lessvoid.nifty.controls.listbox.ListBoxItemController" /> <!-- here we tell Nifty what element to display in each line and the controller is necessary if you want to support selections -->
</control>
So with this in place Nifty will automatically create the "nifty-chat-line" control for each element it needs to display. And with the viewConverterClass we tell Nifty how it should map your model class to that new control. We will take a look at this next but before here is the model class that represents a chat message on the Java side:
/**
* Handles a line in the chat controller. This can be either a chat line or an
* entry in the list of players.
*
* @author Mark
* @version 0.1
*/
public final class ChatEntryModelClass {
private String label;
private NiftyImage icon;
/**
* Constructor excepting the line and the icon.
* @param labelParam The label to put in the entry. This can be either a chat line or a player name.
* @param iconParam The icon to display in the entry, this one is optional.
*/
public ChatEntryModelClass(final String labelParam, final NiftyImage iconParam) {
this.label = labelParam;
this.icon = iconParam;
}
/**
* Return the supplied label. This can be either a chat line or a player name.
* @return The supplied label.
*/
public String getLabel() {
return label;
}
/**
* Return the supplied icon.
* @return The supplied icon.
*/
public NiftyImage getIcon() {
return icon;
}
}
So there is nothing special in here, the ChatEntryModelClass simply contains a label String and a NiftyImage. We can directly add instances of this class to the ListBox and get the selected objects back.
So finally to display a ChatEntryModelClass in the ListBox we need to tell Nifty which field of our class needs to update which element in the ListBox item when it is displayed. We've told Nifty with the viewConverterClass Attribute which class it should use for this. This class needs to implement the ListBox.ListBoxViewConverter interface. The interface is not very complicated, it only consists of two methods. You can find the implementation for the ChatEntryModelClass right here:
/**
* Handles the displaying of the items in the ChatBox.
* @author Mark
* @version 0.1
*/
public class ChatBoxViewConverter implements ListBoxViewConverter<ChatEntryModelClass> {
private static final String CHAT_LINE_ICON = "#chat-line-icon";
private static final String CHAT_LINE_TEXT = "#chat-line-text";
/**
* Default constructor.
*/
public ChatBoxViewConverter() {
}
/**
* {@inheritDoc}
*/
@Override
public final void display(final Element listBoxItem, final ChatEntryModelClass item) {
final Element text = listBoxItem.findElementByName(CHAT_LINE_TEXT);
final TextRenderer textRenderer = text.getRenderer(TextRenderer.class);
final Element icon = listBoxItem.findElementByName(CHAT_LINE_ICON);
final ImageRenderer iconRenderer = icon.getRenderer(ImageRenderer.class);
if (item != null) {
textRenderer.setText(item.getLabel());
iconRenderer.setImage(item.getIcon());
} else {
textRenderer.setText("");
iconRenderer.setImage(null);
}
}
/**
* {@inheritDoc}
*/
@Override
public final int getWidth(final Element listBoxItem, final ChatEntryModelClass item) {
final Element text = listBoxItem.findElementByName(CHAT_LINE_TEXT);
final TextRenderer textRenderer = text.getRenderer(TextRenderer.class);
final Element icon = listBoxItem.findElementByName(CHAT_LINE_ICON);
final ImageRenderer iconRenderer = icon.getRenderer(ImageRenderer.class);
return ((textRenderer.getFont() == null) ? 0 : textRenderer.getFont().getWidth(item.getLabel()))
+ ((item.getIcon() == null) ? 0 : item.getIcon().getWidth());
}
}
This might look a bit complicated but it really is quite simple. Whenever Nifty needs to display a certain ChatEntryModelClass in the ListBox it will call the display() method of the ListBoxViewConverter. It calls this method with the element (this is our control we've defined above) we need to display the ChatEntryModelClass in. Since we know what's part of that control (we've defined it above) it's now just a matter of updating the elements with the text and the image from the ChatEntryModelClass.
The other method we need to implement is the getWidth() method which gets exactly the same parameters, the Element (which represents the ListItem/control we need to update) and the ChatEntryModelClass for this Item. We need to calculate the width of that specific entry in the ListBox so that Nifty knows how width that entry is. Fortunatly for us, we can use some methods that Nifty provides to calculate things like the width of the text for a given font.
When you have created your custom ListItem you might wonder why you can't select them. This is because selecting an item requires a custom effect. When Nifty determines that a certain ListItem has to be selected it will simply start a custom effect with the customKey "select". This allows you to apply any effect to the item when it is in selected state.
In the standard control style it looks like this:
...
<onCustom customKey="select" name="colorBar" post="false" color="#444f" neverStopRendering="true" timeType="infinite" />
<onCustom customKey="select" name="textColor" post="false" color="#fc0f" neverStopRendering="true" timeType="infinite" />
...