From 4ae7a311baba4acb91d94520da375055c2055e96 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 19 Feb 2026 21:42:09 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20JFXTreeView?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/jfoenix/controls/JFXTreeCell.java | 146 ++++++++++++++++++ .../com/jfoenix/controls/JFXTreeView.java | 48 ++++++ 2 files changed, 194 insertions(+) create mode 100644 HMCL/src/main/java/com/jfoenix/controls/JFXTreeCell.java create mode 100644 HMCL/src/main/java/com/jfoenix/controls/JFXTreeView.java diff --git a/HMCL/src/main/java/com/jfoenix/controls/JFXTreeCell.java b/HMCL/src/main/java/com/jfoenix/controls/JFXTreeCell.java new file mode 100644 index 0000000000..e415743338 --- /dev/null +++ b/HMCL/src/main/java/com/jfoenix/controls/JFXTreeCell.java @@ -0,0 +1,146 @@ +/* + * 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.controls; + +import com.jfoenix.utils.JFXNodeUtils; +import javafx.beans.InvalidationListener; +import javafx.beans.WeakInvalidationListener; +import javafx.geometry.Insets; +import javafx.scene.Node; +import javafx.scene.control.TreeCell; +import javafx.scene.control.TreeItem; +import javafx.scene.layout.*; +import javafx.scene.paint.Color; + +import java.lang.ref.WeakReference; + +/// JFXTreeCell is simple material design implementation of a tree cell. +/// +/// @author Shadi Shaheen +/// @version 1.0 +/// @since 2017-02-15 +public class JFXTreeCell extends TreeCell { + + protected JFXRippler cellRippler = new JFXRippler(this) { + @Override + protected Node getMask() { + Region clip = new Region(); + JFXNodeUtils.updateBackground(JFXTreeCell.this.getBackground(), clip); + double width = control.getLayoutBounds().getWidth(); + double height = control.getLayoutBounds().getHeight(); + clip.resize(width, height); + return clip; + } + + @Override + protected void positionControl(Node control) { + // do nothing + } + }; + private HBox hbox; + private final StackPane selectedPane = new StackPane(); + + private final InvalidationListener treeItemGraphicInvalidationListener = observable -> updateDisplay(getItem(), + isEmpty()); + private final WeakInvalidationListener weakTreeItemGraphicListener = new WeakInvalidationListener( + treeItemGraphicInvalidationListener); + + private WeakReference> treeItemRef; + + public JFXTreeCell() { + selectedPane.getStyleClass().add("selection-bar"); + selectedPane.setBackground(new Background(new BackgroundFill(Color.RED, CornerRadii.EMPTY, Insets.EMPTY))); + selectedPane.setPrefWidth(3); + selectedPane.setMouseTransparent(true); + selectedProperty().addListener((o, oldVal, newVal) -> selectedPane.setVisible(newVal ? true : false)); + + final InvalidationListener treeItemInvalidationListener = observable -> { + TreeItem oldTreeItem = treeItemRef == null ? null : treeItemRef.get(); + if (oldTreeItem != null) { + oldTreeItem.graphicProperty().removeListener(weakTreeItemGraphicListener); + } + + TreeItem newTreeItem = getTreeItem(); + if (newTreeItem != null) { + newTreeItem.graphicProperty().addListener(weakTreeItemGraphicListener); + treeItemRef = new WeakReference<>(newTreeItem); + } + }; + final WeakInvalidationListener weakTreeItemListener = new WeakInvalidationListener(treeItemInvalidationListener); + treeItemProperty().addListener(weakTreeItemListener); + if (getTreeItem() != null) { + getTreeItem().graphicProperty().addListener(weakTreeItemGraphicListener); + } + } + + @Override + protected void layoutChildren() { + super.layoutChildren(); + if (!getChildren().contains(selectedPane)) { + getChildren().add(0, cellRippler); + cellRippler.rippler.clear(); + getChildren().add(0, selectedPane); + } + cellRippler.resizeRelocate(0, 0, getWidth(), getHeight()); + cellRippler.releaseRipple(); + selectedPane.resizeRelocate(0, 0, selectedPane.prefWidth(-1), getHeight()); + selectedPane.setVisible(isSelected()); + } + + private void updateDisplay(T item, boolean empty) { + if (item == null || empty) { + hbox = null; + setText(null); + setGraphic(null); + } else { + TreeItem treeItem = getTreeItem(); + if (treeItem != null && treeItem.getGraphic() != null) { + if (item instanceof Node) { + setText(null); + if (hbox == null) { + hbox = new HBox(3); + } + hbox.getChildren().setAll(treeItem.getGraphic(), (Node) item); + setGraphic(hbox); + } else { + hbox = null; + setText(item.toString()); + setGraphic(treeItem.getGraphic()); + } + } else { + hbox = null; + if (item instanceof Node) { + setText(null); + setGraphic((Node) item); + } else { + setText(item.toString()); + setGraphic(null); + } + } + } + } + + @Override + protected void updateItem(T item, boolean empty) { + super.updateItem(item, empty); + updateDisplay(item, empty); + setMouseTransparent(item == null || empty); + } +} diff --git a/HMCL/src/main/java/com/jfoenix/controls/JFXTreeView.java b/HMCL/src/main/java/com/jfoenix/controls/JFXTreeView.java new file mode 100644 index 0000000000..5c2a536da2 --- /dev/null +++ b/HMCL/src/main/java/com/jfoenix/controls/JFXTreeView.java @@ -0,0 +1,48 @@ +/* + * 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.controls; + +import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeView; + +/// JFXTreeView is the material design implementation of a TreeView +/// with expand/collapse animation and selection indicator. +/// +/// @author Shadi Shaheen +/// @version 1.0 +/// @since 2017-02-15 +public class JFXTreeView extends TreeView { + + private static final String DEFAULT_STYLE_CLASS = "jfx-tree-view"; + + public JFXTreeView() { + init(); + } + + public JFXTreeView(TreeItem root) { + super(root); + init(); + } + + private void init() { + this.setCellFactory((view) -> new JFXTreeCell<>()); + this.getStyleClass().add(DEFAULT_STYLE_CLASS); + } +} From 6cfbc43c1cbff8497cdd2497606e44ac0dc42bd7 Mon Sep 17 00:00:00 2001 From: Glavo Date: Thu, 19 Feb 2026 21:46:09 +0800 Subject: [PATCH 2/2] update --- .../treetable/RecursiveTreeObject.java | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 HMCL/src/main/java/com/jfoenix/controls/datamodels/treetable/RecursiveTreeObject.java diff --git a/HMCL/src/main/java/com/jfoenix/controls/datamodels/treetable/RecursiveTreeObject.java b/HMCL/src/main/java/com/jfoenix/controls/datamodels/treetable/RecursiveTreeObject.java new file mode 100644 index 0000000000..8b210f6ed9 --- /dev/null +++ b/HMCL/src/main/java/com/jfoenix/controls/datamodels/treetable/RecursiveTreeObject.java @@ -0,0 +1,80 @@ +/* + * 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.controls.datamodels.treetable; + +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.control.TreeTableColumn; + +/// data model that is used in JFXTreeTableView, it's used to implement +/// the grouping feature. +/// +/// **Note:** the data object used in JFXTreeTableView **must** extends this class +/// +/// @param is the concrete object of the Tree table +/// @author Shadi Shaheen +/// @version 1.0 +/// @since 2016-03-09 +public class RecursiveTreeObject { + + /// grouped children objects + private ObservableList children = FXCollections.observableArrayList(); + + public ObservableList getChildren() { + return children; + } + + public void setChildren(ObservableList children) { + this.children = children; + } + + /// Whether or not the object is grouped by a specified tree table column + ObjectProperty> groupedColumn = new SimpleObjectProperty<>(); + + public final ObjectProperty> groupedColumnProperty() { + return this.groupedColumn; + } + + public final TreeTableColumn getGroupedColumn() { + return this.groupedColumnProperty().get(); + } + + public final void setGroupedColumn(final TreeTableColumn groupedColumn) { + this.groupedColumnProperty().set(groupedColumn); + } + + /// the value that must be shown when grouped + ObjectProperty groupedValue = new SimpleObjectProperty<>(); + + public final ObjectProperty groupedValueProperty() { + return this.groupedValue; + } + + public final Object getGroupedValue() { + return this.groupedValueProperty().get(); + } + + public final void setGroupedValue(final Object groupedValue) { + this.groupedValueProperty().set(groupedValue); + } + +}