I wasn’t able to get the skin to work (but very happy for someone with more JavaFx knowledge to take a look at it) so I’m going to settle for what I have. I’ve put a PR in if people would like the code added to jme-jfx-11 https://github.com/jayfella/jme-jfx-11/pull/7
Alternatively, I’ve produced a (far less clean, reflection filled) stand alone version that will work without changes to jme-jfx-11
import com.jayfella.jme.jfx.JavaFxUI;
import com.jme3.app.Application;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import java.lang.reflect.Field;
/**
* This is a JME alternative for the ComboBox.
*
* It behaves broadly the same way as the core ComboBox, but is not feature complete.
*
* In particular:
* It doesn't fully support keyboard selection (a mouse click is needed to close it)
* It doesn't support sizing based on number of entries, only by pixels
* It can go "offscreen" when it tries to open near the bottom of the screen
*
* @param <T>
*/
public class ComboBoxJMEBackCompat<T> extends ComboBox<T> {
/**
* This is for any comboBox
*/
private static Runnable removeExistingPopup = () -> {};
/**
* This is for this combobox
*/
Runnable removeListPopup;
int maxHeight = 200;
@Override
public void show() {
Bounds boundsInScene = localToScene(getBoundsInLocal());
ListView<T> items = new ListView<>();
items.setItems(this.getItems());
items.setMinWidth(boundsInScene.getWidth());
items.setMaxHeight(maxHeight);
removeListPopup = attachPopup(items, boundsInScene.getMinX(), boundsInScene.getMaxY());
items.setOnMousePressed(event -> {
getSelectionModel().select(items.getSelectionModel().getSelectedItem());
removeListPopup.run();
});
}
@Override
public void hide() {
//do nothing, we're handling our own open/close (although keyboard based selection might need this?)
}
/**
* Sets the height of the combobox when it opens (in pixels)
* @param height
*/
public void setListHeight(int height){
this.maxHeight = height;
}
private static Runnable attachPopup(javafx.scene.Node node, double x, double y){
Application app = (Application)getJavaFxUIPrivateMember("app");
Group group = (Group)getJavaFxUIPrivateMember("group");
removeExistingPopup.run();
AnchorPane popupOverlay = new AnchorPane();
popupOverlay.setMinWidth(app.getCamera().getWidth());
popupOverlay.setMinHeight(app.getCamera().getHeight());
popupOverlay.getChildren().add(node);
node.setTranslateX(x);
node.setTranslateY(y);
group.getChildren().add(popupOverlay);
removeExistingPopup = () -> {
group.getChildren().remove(popupOverlay);
removeExistingPopup = () -> {};
};
popupOverlay.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> {
removeExistingPopup.run();
});
return removeExistingPopup;
}
private static Object getJavaFxUIPrivateMember(String memberName){
try {
JavaFxUI instance = JavaFxUI.getInstance();
Field declaredField = instance.getClass().getDeclaredField(memberName);
declaredField.setAccessible(true);
return declaredField.get(instance);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
(If anyone wants to use this please consider it in the public domain)