I've noticed some issues when a ComboBox with a custom skin is used in a TabPane with a custom skin. Note: The issue occurs only if the ComboBox skin is specified in an external CSS file.
- the arrow button is not placed correct, it appears in the top left corner (looks like it has no parent)
- the selected entry does not appear
- the width of the ComboBox is too small
- the NPE issue on focus has been already posted - seeJDK-8185854
Please find the related test below - tested with Java 9u175
package test.combobox;
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.skin.ComboBoxBaseSkin;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ComboTabPaneTest extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
@Override
public void start(Stage stage)
{
Application.setUserAgentStylesheet(getClass().getResource("/test/combobox/style.css").toExternalForm());
stage.setScene(new Scene(createContent()));
stage.setTitle(getClass().getSimpleName());
stage.show();
}
private Parent createContent()
{
TabPane tabPane = new TabPane();
tabPane.getTabs().add(new Tab("Tab", createTabContent()));
// Note: update package if not in test.combobox
tabPane.setStyle("-fx-skin: 'test.combobox.ComboTabPaneTest$TabPaneSkin'");
BorderPane content = new BorderPane();
content.setCenter(tabPane);
return content;
}
private Node createTabContent()
{
FlowPane p = new FlowPane();
p.setPadding(new Insets(40));
ComboBox<String> combo = new ComboBox<>();
//combo.setStyle("-fx-skin: 'test.combobox.ComboTabPaneTest$ComboBoxListViewSkin'");
combo.setItems(FXCollections.<String>observableArrayList("Regular", "ReadOnly", "Disabled", "Disabled/ReadOnly"));
combo.setDisable(false);
combo.setEditable(false);
combo.setValue("ReadOnly");
p.getChildren().add(combo);
return p;
}
// TabPaneSkin class
public static class TabPaneSkin extends javafx.scene.control.skin.TabPaneSkin
{
public TabPaneSkin(TabPane tabPane)
{
super(tabPane);
}
}
// ComboBoxSkin class
public static class ComboBoxListViewSkin<T> extends javafx.scene.control.skin.ComboBoxListViewSkin<T>
{
public ComboBoxListViewSkin(ComboBox<T> combo)
{
super(combo);
StackPane arrowButton = null;
try
{
Field field = ComboBoxBaseSkin.class.getDeclaredField("arrowButton");
field.setAccessible(true);
arrowButton = (StackPane)field.get(this);
System.err.println("Parent of arrow-button: " + arrowButton.getParent());
}
catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
}
}
}
style.css:
.root {
-fx-font: 11 Tahoma;
-fx-text-fill: black;
}
.text {
-fx-text-fill: #F00;
-fx-font-smoothing-type: lcd;
}
.combo-box{
-fx-skin: "test.combobox.ComboTabPaneTest$ComboBoxListViewSkin";
}
.combo-box-base{
-fx-background-color: #0FF;
-fx-padding: 2 2;
}
.combo-box-base:editable{
-fx-background-color: #FF0;
}
.combo-box-base:editable > .text-field {
-fx-border-image-source: null;
-fx-background-color: transparent;
-fx-padding: 1 0;
}
.combo-box-base > .arrow-button {
-fx-background-color: red;
-fx-padding: 0 4;
}
.combo-box-base > .arrow-button > .arrow {
-fx-background-color: black;
-fx-background-insets: 0 0 0 0;
-fx-padding: 2 4;
-fx-shape: "M 0 0 H 7 L 3.5 4 z";
}
/* -------------- STYLES FOR THE DEFAULT LISTVIEW-BASED COMBOBOX ------------- */
.combo-box-popup > .list-view {
-fx-border-style: solid;
-fx-border-width: 1;
-fx-border-color: black;
-fx-background-color: white;
-fx-background-insets: 0, 1;
}
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell {
-fx-padding: 1 10;
/*-fx-background-color: red;*/
/*-fx-cell-size: 10;*/
}
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
-fx-background: green;
-fx-background-color: orange !important;
}
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover,
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover {
-fx-background-color: silver !important;
}
.combo-box-popup > .list-view > .placeholder > .label {
-fx-text-fill: blue;
}
- the arrow button is not placed correct, it appears in the top left corner (looks like it has no parent)
- the selected entry does not appear
- the width of the ComboBox is too small
- the NPE issue on focus has been already posted - see
Please find the related test below - tested with Java 9u175
package test.combobox;
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.skin.ComboBoxBaseSkin;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class ComboTabPaneTest extends Application
{
public static void main(String[] args)
{
Application.launch(args);
}
@Override
public void start(Stage stage)
{
Application.setUserAgentStylesheet(getClass().getResource("/test/combobox/style.css").toExternalForm());
stage.setScene(new Scene(createContent()));
stage.setTitle(getClass().getSimpleName());
stage.show();
}
private Parent createContent()
{
TabPane tabPane = new TabPane();
tabPane.getTabs().add(new Tab("Tab", createTabContent()));
// Note: update package if not in test.combobox
tabPane.setStyle("-fx-skin: 'test.combobox.ComboTabPaneTest$TabPaneSkin'");
BorderPane content = new BorderPane();
content.setCenter(tabPane);
return content;
}
private Node createTabContent()
{
FlowPane p = new FlowPane();
p.setPadding(new Insets(40));
ComboBox<String> combo = new ComboBox<>();
//combo.setStyle("-fx-skin: 'test.combobox.ComboTabPaneTest$ComboBoxListViewSkin'");
combo.setItems(FXCollections.<String>observableArrayList("Regular", "ReadOnly", "Disabled", "Disabled/ReadOnly"));
combo.setDisable(false);
combo.setEditable(false);
combo.setValue("ReadOnly");
p.getChildren().add(combo);
return p;
}
// TabPaneSkin class
public static class TabPaneSkin extends javafx.scene.control.skin.TabPaneSkin
{
public TabPaneSkin(TabPane tabPane)
{
super(tabPane);
}
}
// ComboBoxSkin class
public static class ComboBoxListViewSkin<T> extends javafx.scene.control.skin.ComboBoxListViewSkin<T>
{
public ComboBoxListViewSkin(ComboBox<T> combo)
{
super(combo);
StackPane arrowButton = null;
try
{
Field field = ComboBoxBaseSkin.class.getDeclaredField("arrowButton");
field.setAccessible(true);
arrowButton = (StackPane)field.get(this);
System.err.println("Parent of arrow-button: " + arrowButton.getParent());
}
catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e)
{
e.printStackTrace();
}
}
}
}
style.css:
.root {
-fx-font: 11 Tahoma;
-fx-text-fill: black;
}
.text {
-fx-text-fill: #F00;
-fx-font-smoothing-type: lcd;
}
.combo-box{
-fx-skin: "test.combobox.ComboTabPaneTest$ComboBoxListViewSkin";
}
.combo-box-base{
-fx-background-color: #0FF;
-fx-padding: 2 2;
}
.combo-box-base:editable{
-fx-background-color: #FF0;
}
.combo-box-base:editable > .text-field {
-fx-border-image-source: null;
-fx-background-color: transparent;
-fx-padding: 1 0;
}
.combo-box-base > .arrow-button {
-fx-background-color: red;
-fx-padding: 0 4;
}
.combo-box-base > .arrow-button > .arrow {
-fx-background-color: black;
-fx-background-insets: 0 0 0 0;
-fx-padding: 2 4;
-fx-shape: "M 0 0 H 7 L 3.5 4 z";
}
/* -------------- STYLES FOR THE DEFAULT LISTVIEW-BASED COMBOBOX ------------- */
.combo-box-popup > .list-view {
-fx-border-style: solid;
-fx-border-width: 1;
-fx-border-color: black;
-fx-background-color: white;
-fx-background-insets: 0, 1;
}
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell {
-fx-padding: 1 10;
/*-fx-background-color: red;*/
/*-fx-cell-size: 10;*/
}
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected {
-fx-background: green;
-fx-background-color: orange !important;
}
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:hover,
.combo-box-popup > .list-view > .virtual-flow > .clipped-container > .sheet > .list-cell:filled:selected:hover {
-fx-background-color: silver !important;
}
.combo-box-popup > .list-view > .placeholder > .label {
-fx-text-fill: blue;
}
- relates to
-
JDK-8185854 NPE on non-editable ComboBox in TabPane with custom Skin
-
- Resolved
-