/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.jfoenix.skins; import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject; import com.sun.javafx.scene.control.skin.TreeTableRowSkin; import javafx.animation.*; import javafx.animation.Animation.Status; import javafx.beans.value.ChangeListener; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.control.Control; import javafx.scene.control.TreeTableRow; import javafx.util.Duration; import java.lang.reflect.Field; import java.util.Map; /** * @author Shadi Shaheen */ public class JFXTreeTableRowSkin<T> extends TreeTableRowSkin<T> { static Map<Control, Double> disclosureWidthMap = null; // this vairable is used to hold the expanded/collapsed row index private static int expandedIndex = -1; // this variable indicates whether an expand/collapse operation is triggered private boolean expandTriggered = false; private ChangeListener<Boolean> expandedListener = (o, oldVal, newVal) -> { if (getSkinnable().getTreeItem() != null && !getSkinnable().getTreeItem().isLeaf()) { expandedIndex = getSkinnable().getIndex(); expandTriggered = true; } }; private Timeline collapsedAnimation; private Animation expandedAnimation; public JFXTreeTableRowSkin(TreeTableRow<T> control) { super(control); if (disclosureWidthMap == null) { try { Field declaredField = getClass().getSuperclass() .getSuperclass() .getDeclaredField("maxDisclosureWidthMap"); declaredField.setAccessible(true); disclosureWidthMap = (Map<Control, Double>) declaredField.get(this); } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException ex) { ex.printStackTrace(); } } getSkinnable().indexProperty().addListener((o, oldVal, newVal) -> { if (newVal.intValue() != -1) { if (newVal.intValue() == expandedIndex) { expandTriggered = true; expandedIndex = -1; } else { expandTriggered = false; } } }); // clear disclosure node indentation after grouping/ungrouping getSkinnable().getTreeTableView().rootProperty().addListener((o, oldVal, newVal) -> { disclosureWidthMap.remove(getSkinnable().getTreeTableView()); }); } @Override protected void layoutChildren(final double x, final double y, final double w, final double h) { // allow custom skin to grouped rows getSkinnable().getStyleClass().remove("tree-table-row-group"); if (getSkinnable().getTreeItem() != null && getSkinnable().getTreeItem() .getValue() instanceof RecursiveTreeObject && getSkinnable() .getTreeItem() .getValue() .getClass() == RecursiveTreeObject.class) { getSkinnable().getStyleClass().add("tree-table-row-group"); } if (getSkinnable().getIndex() > -1 && getSkinnable().getTreeTableView() .getTreeItem(getSkinnable().getIndex()) != null) { super.layoutChildren(x, y, w, h); // disclosure row case if (getSkinnable().getTreeItem() != null && !getSkinnable().getTreeItem().isLeaf()) { Node arrow = ((Parent) getDisclosureNode()).getChildrenUnmodifiable().get(0); // relocating the disclosure node according to the grouping column final Parent arrowParent = arrow.getParent(); if (((RecursiveTreeObject<?>) getSkinnable().getItem()).getGroupedColumn() != null) { Node col = getChildren().get(getSkinnable().getTreeTableView() .getTreeItemLevel(getSkinnable().getTreeItem()) + 1); if (getSkinnable().getItem() instanceof RecursiveTreeObject) { int index = getSkinnable().getTreeTableView() .getColumns() .indexOf(((RecursiveTreeObject<?>) getSkinnable().getItem()).getGroupedColumn()); // getSkinnable().getTreeTableView().getColumns().get(index).getText(); col = getChildren().get(index + 1); // index + 2 , if the rippler was added } arrowParent.setTranslateX(col.getBoundsInParent().getMinX()); arrowParent.setLayoutX(0); } else { arrowParent.setTranslateX(0); arrowParent.setLayoutX(0); } // add disclosure node animation if (expandedAnimation == null || !(expandedAnimation.getStatus() == Status.RUNNING)) { expandedAnimation = new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(arrow.rotateProperty(), 90, Interpolator.EASE_BOTH))); expandedAnimation.setOnFinished((finish) -> arrow.setRotate(90)); } if (collapsedAnimation == null || !(collapsedAnimation.getStatus() == Status.RUNNING)) { collapsedAnimation = new Timeline(new KeyFrame(Duration.millis(160), new KeyValue(arrow.rotateProperty(), 0, Interpolator.EASE_BOTH))); collapsedAnimation.setOnFinished((finish) -> arrow.setRotate(0)); } getSkinnable().getTreeItem().expandedProperty().removeListener(expandedListener); getSkinnable().getTreeItem().expandedProperty().addListener(expandedListener); if (expandTriggered) { if (getSkinnable().getTreeTableView().getTreeItem(getSkinnable().getIndex()).isExpanded()) { arrow.setRotate(0); expandedAnimation.play(); } else { arrow.setRotate(90); collapsedAnimation.play(); } expandTriggered = false; } else { if (getSkinnable().getTreeTableView().getTreeItem(getSkinnable().getIndex()).isExpanded()) { if (expandedAnimation.getStatus() != Status.RUNNING) { arrow.setRotate(90); } } else { if (collapsedAnimation.getStatus() != Status.RUNNING) { arrow.setRotate(0); } } } } } } @Override protected double getIndentationPerLevel() { return super.getIndentationPerLevel(); } }