Environment: JDK 7u75, Windows 8.1 x64, JavaFX2.2
Sample code:
public class TreeViewSample extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws MalformedURLException {
primaryStage.setTitle("Tree View Sample");
TreeItem<String> rootItem = new TreeItem<String>("RootNode");
for (int i = 1; i < 5; i++) {
TreeItem<String> item = new TreeItem<String>("SubNode" + i);
rootItem.getChildren().add(item);
item.getChildren().add(new TreeItem<String>("SubSubNode" + i + "" + i));
}
rootItem.getChildren().add(new TreeItem<String>("SecondRootNode"));
TreeView<String> tree = new TreeView<String>(rootItem);
StackPane root = new StackPane();
root.getChildren().add(tree);
Scene scene = new Scene(root, 300, 250);
scene.getStylesheets().add((new File("../css/styletest.css").toURI()).toURL().toString());
primaryStage.setScene(scene);
primaryStage.show();
}
}
CSS:
.tree-cell {
-fx-skin: "com.sun.javafx.scene.control.skin.TreeCellSkin";
-fx-background-color: -fx-control-inner-background;
-fx-padding: 0.25em; /* 3 */
-fx-text-fill: -fx-text-inner-color;
-fx-indent: 10;
}
.tree-cell .label {
-fx-padding: 0.0em 0.0em 0.0em 0.25em; /* 0 0 0 3 */
}
.tree-cell .tree-disclosure-node {
-fx-padding: 4 2 4 8;
-fx-background-color: transparent;
}
.tree-cell .tree-disclosure-node .arrow {
-fx-background-color: -fx-mark-color;
-fx-padding: 0.333333em; /* 4 */
-fx-shape: "M 0 -4 L 8 0 L 0 4 z";
}
We need to delete all space and arrows for all nodes. Modified CSS:
.tree-cell {
-fx-skin: "com.sun.javafx.scene.control.skin.TreeCellSkin";
-fx-background-color: -fx-control-inner-background;
-fx-padding: 0px;
-fx-text-fill: -fx-text-inner-color;
-fx-indent: 0px;
}
.tree-cell .label {
-fx-padding: 0.0em 0.0em 0.0em 0.0em;
}
.tree-cell .tree-disclosure-node {
-fx-padding: 0px;
-fx-background-color: transparent;
}
.tree-cell .tree-disclosure-node .arrow {
-fx-background-color: -fx-mark-color;
-fx-padding: 0.0em;
}
As you see there is no padding and indent for all nodes except leaves.
And the question - How to delete\modify this padding?
In this case, the JavaFX developers made the standard length of 18 and did not provide an opportunity to change it. Here is their comment:
/*
* This is rather hacky - but it is a quick workaround to resolve the
* issue that we don't know maximum width of a disclosure node for a given
* TreeView. If we don't know the maximum width, we have no way to ensure
* consistent indentation for a given TreeView.
*
* To work around this, we create a single WeakHashMap to store a max
* disclosureNode width per TreeView. We use WeakHashMap to help prevent
* any memory leaks.
*
* RT-19656 identifies a related issue, which is that we may not provide
* indentation to any TreeItems because we have not yet encountered a cell
* which has a disclosureNode. Once we scroll and encounter one, indentation
* happens in a displeasing way.
*/
But if it's really necessary, then we override the TreeCellSkin class and the layoutChildren method using reflection. But this is not safe:
final double defaultDisclosureWidth = maxDisclosureWidthMap.containsKey(tree) ?
maxDisclosureWidthMap.get(tree) : 18; // RT-19656: default width of default disclosure node
on
final double defaultDisclosureWidth = 0;
Full class:
public class CustomTreeCellSkin<T> extends TreeCellSkin<T> {
public CustomTreeCellSkin(TreeCell control) {
super(control);
}
private boolean disclosureNodeDirty = true;
@Override
protected void layoutChildren(double x, double y, double w, double h) {
try {
TreeView<T> tree = getSkinnable().getTreeView();
if (tree == null) return;
if (disclosureNodeDirty) {
Method method =TreeCellSkin.class.getDeclaredMethod("updateDisclosureNode");
method.setAccessible(true);
method.invoke(this);
disclosureNodeDirty=false;
}
Node disclosureNode = getSkinnable().getDisclosureNode();
TreeItem<?> treeItem = getSkinnable().getTreeItem();
int level = tree.getTreeItemLevel(treeItem);
if (!tree.isShowRoot()) level--;
double leftMargin = getIndent() * level;
x += leftMargin;
// position the disclosure node so that it is at the proper indent
boolean disclosureVisible = disclosureNode != null && treeItem != null && !treeItem.isLeaf();
Field field = TreeCellSkin.class.getDeclaredField("maxDisclosureWidthMap");
field.setAccessible(true);
Map<TreeView<?>, Double> maxDisclosureWidthMap = (Map<TreeView<?>, Double>) field.get(this);
final double defaultDisclosureWidth = 0; // RT-19656: default width of default disclosure node
double disclosureWidth = defaultDisclosureWidth;
if (disclosureVisible) {
if (disclosureNode == null || disclosureNode.getScene() == null) {
updateChildren();
}
if (disclosureNode != null) {
disclosureWidth = disclosureNode.prefWidth(h);
if (disclosureWidth > defaultDisclosureWidth) {
maxDisclosureWidthMap.put(tree, disclosureWidth);
}
double ph = disclosureNode.prefHeight(disclosureWidth);
disclosureNode.resize(disclosureWidth, ph);
positionInArea(disclosureNode, x, y,
disclosureWidth, ph, /*baseline ignored*/0,
HPos.CENTER, VPos.CENTER);
}
}
// determine starting point of the graphic or cell node, and the
// remaining width available to them
final int padding = treeItem != null && treeItem.getGraphic() == null ? 0 : 3;
x += disclosureWidth + padding;
w -= (leftMargin + disclosureWidth + padding);
// Rather ugly fix for RT-38519, where graphics are disappearing in
// certain circumstances
Node graphic = getSkinnable().getGraphic();
if (graphic != null && !getChildren().contains(graphic)) {
getChildren().add(graphic);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
layoutLabelInArea(x, y, w, h);
}
}
In css in this case, you need to specify our overridden class:
.tree-cell {
-fx-skin: "CustomTreeCellSkin";
-fx-background-color: -fx-control-inner-background;
-fx-padding: 0px;
-fx-text-fill: -fx-text-inner-color;
-fx-indent: 0px;
}
.tree-cell .label {
-fx-padding: 0.0em 0.0em 0.0em 0.0em;
}
.tree-cell .tree-disclosure-node {
-fx-padding: 0px;
-fx-background-color: transparent;
}
.tree-cell .tree-disclosure-node .arrow {
-fx-background-color: -fx-mark-color;
-fx-padding: 0.0em;
}
Sorry for my English. =)
While I really appreciate aforementioned solution it's too complicated, esp because it uses reflection and with JPMS reflection usage have to be allowed explicitly.
Since defaultDisclosureWidth
fallback value is hardcoded, we could just negate it by padding.
public static class CustomTreeCell extends TreeCell<Foo> {
static final PseudoClass LEAF = PseudoClass.getPseudoClass("leaf");
@Override
protected void updateItem(Foo item, boolean empty) {
super.updateItem(item, empty);
// ...
pseudoClassStateChanged(LEAF, getTreeItem().isLeaf());
}
}
.tree-cell {
-fx-background-insets: 0;
-fx-padding: 0;
-fx-indent: 0;
}
.tree-cell:leaf {
-fx-padding: 0 0 0 -18px;
}
.tree-cell .tree-disclosure-node,
.tree-cell .arrow {
-fx-min-width: 0;
-fx-pref-width: 0;
-fx-max-width: 0;
-fx-min-height: 0;
-fx-pref-height: 0;
-fx-max-height: 0;
}
Use -fx-indent
property to set whatever padding you want.
Simple verification:
TreeItem<Foo> nodeRoot = new TreeItem<>(new Foo("root"));
TreeItem<Foo> nodeTest1 = new TreeItem<>(new Foo("test1"));
nodeRoot.getChildren().add(nodeTest1);
TreeItem<Foo> nodeTest2 = new TreeItem<>(new Foo("test2"));
nodeTest1.getChildren().add(nodeTest2);
TreeItem<Foo> nodeTest3 = new TreeItem<>(new Foo("test3"));
nodeTest2.getChildren().add(nodeTest3);
TreeItem<Foo> nodeTest4 = new TreeItem<>(new Foo("test4"));
nodeTest3.getChildren().add(nodeTest4);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With