Saturday, January 06, 2007

NetBeans Platform: Combobox in property editor

Happy new year everyone !

I'm going to talk today about something simple GUI-wise but quite a lot asked on the mailing lists: how does one display a combobox in the property editor ? Actually, how does one use an editor that displays a combobox.

What should get you started with this task (and other editor customizing) is a little file from the Platform javadoc.

This is what we plan to get:

In order to do this note the Custom parameters in core editors paragraph from the above link and the fact that for java.lang.Integer properties there are some custom keys we can use:
  • stringKeys - an array of strings to be present in the combobox
  • intValues - an array of integers representing the values of the selection in the combobox.
So something like this:

Node.Property p=new YourProperty(Integer.class);
p.setValue("intValues",new int[]{1,2,3,4});
p.setValue("stringKeys",new String[]{"One","Two","Three","Four"});

is all you need.

Note: Please take care that for the current property
  • #getValue is the index in the intValues array. So if you want to see "Three" in the combobox, you return the number 2 in #getValue.
  • #setValue is called with the actual number from intValues (not the index). So if the user selects "Four" from the combobox, #setValue(4) will be called.


marvi said...

What about if you use BeanInfo? I have this in my BeanInfo class:

PropertyDescriptor layout = new PropertyDescriptor("layout", beanClass);
layout.setValue("enumerationValues", new Object[] {
"Body", new Integer(LayoutProperties.BODY), "LayoutProperties.BODY",
"Header", new Integer(LayoutProperties.HEADER), "LayoutProperties.HEADER",
"Sidebar", new Integer(LayoutProperties.SIDEBAR), "LayoutProperties.SIDEBAR",
"Footer", new Integer(LayoutProperties.FOOTER), "LayoutProperties.FOOTER" } );

This does not show in in the Netbeans PropertySheet. It only shown an input field for Integers.

I82Much said...

I don't understand why this is set up the way it is... say I have a bidirectional mapping between Strings and numbers; {20=>"A", 30=>"B"}. I want user to be able to choose between A and B and under the hood the value is set to 20 or 30. As it stands, I can't do this; I get an Array Out Of Bounds exception. Why the heck would the getValue return index and setValue be actual number? It violates symmetry, and it makes it impossible to use for my use case.

Emilian Bold said...

@I82Much Yeah, it's a bit odd hence my note underlining this issue.

Not sure what's the reason but it's not that much of a hurdle. You could always have a 1=>"a", 2=>"b" mapping for the Node.Property and you map those 1,2 numbers to your other array (10, 20, etc).

I82Much said...

I don't believe that is an acceptable workaround, or I'm not quite understanding what you mean.

My Node has a certain Type field, which is an integer. Its value means something; I can't replace it with the ordinal mapping just so it displays correctly in the PropertySheetView. Unfortunately the type enumeration has holes and can't just be expressed in ordinal mapping from 0 .. n-1, as this requires for intuitive functionality.

I personally think the asymmetric functionality doesn't make any sense, and I can't think of any drawbacks to allowing the more intuitive functionality (other than breaking existing client code).

Since I don't want to muck around with having different mapping arrays to get the default functionality to work, do you know best way to modify a PropertyEditor to encompass the functionality I need?

I82Much said...

Ignore my last comment, I fixed it myself.

I found an early version of the code

and modified it. The main change is detailed here:

Note that I also had to change the key/value declarations.

keys = (String[]) env.getFeatureDescriptor().getValue(
"stringKeys"); //NOI18N
values = (int[]) env.getFeatureDescriptor().getValue(
"intValues"); //NOI18N
// Either both or none of the keys/values must be provided.
assert (keys == null) == (values == null);