/** * SimpleSliderClient.java * * Copyright (c) 2013-2016, F(X)yz * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of F(X)yz, any associated website, nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL F(X)yz BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.fxyz3d.ExtrasAndTests; import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Map; import javafx.beans.Observable; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.geometry.Insets; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.control.TreeCell; import javafx.scene.control.TreeItem; import javafx.scene.control.TreeView; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javafx.stage.Stage; import javafx.util.Callback; import org.fxyz3d.FXyzSample; import org.fxyz3d.FXyzSampleBase; import org.fxyz3d.model.EmptySample; import org.fxyz3d.model.Project; import org.fxyz3d.model.SampleTree; import org.fxyz3d.model.WelcomePage; import org.fxyz3d.util.SampleScanner; /** * * @author Jason Pollastrini aka jdub1581 */ public class SimpleSliderClient extends AbstractPopoutClient { private TreeItem<FXyzSample> root; private final Map<String, Project> projectsMap; private FXyzSample selectedSample; private TreeView<FXyzSample> contentTree; private TextField searchBar; public SimpleSliderClient(Stage stage, boolean popsEnabled) { super(stage, popsEnabled); this.projectsMap = new SampleScanner().discoverSamples(); buildProjectTree(null); VBox treeMenu = new VBox(); treeMenu.setFillWidth(true); treeMenu.setSpacing(3); treeMenu.setPadding(new Insets(3)); contentTree = new TreeView<>(root); contentTree.setFocusTraversable(false); searchBar = new TextField(); searchBar.setFocusTraversable(false); searchBar.setMaxWidth(USE_COMPUTED_SIZE); searchBar.getStyleClass().add("fxyz3d-search-box"); searchBar.textProperty().addListener((Observable o) -> { buildProjectTree(searchBar.getText()); }); treeMenu.getChildren().addAll(searchBar, contentTree); VBox.setVgrow(contentTree, Priority.ALWAYS); menuPane.getChildren().add(treeMenu); menuPane.setPrefSize(USE_COMPUTED_SIZE, USE_COMPUTED_SIZE); contentTree.setShowRoot(false); contentTree.getStyleClass().add("samples-tree"); contentTree.setMinWidth(USE_PREF_SIZE); contentTree.setMaxWidth(Double.MAX_VALUE); contentTree.setCellFactory(new Callback<TreeView<FXyzSample>, TreeCell<FXyzSample>>() { @Override public TreeCell<FXyzSample> call(TreeView<FXyzSample> param) { return new TreeCell<FXyzSample>() { @Override protected void updateItem(FXyzSample item, boolean empty) { super.updateItem(item, empty); if (empty) { setText(""); } else { setText(item.getSampleName()); } } }; } }); contentTree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<TreeItem<FXyzSample>>() { @Override public void changed(ObservableValue<? extends TreeItem<FXyzSample>> observable, TreeItem<FXyzSample> oldValue, TreeItem<FXyzSample> newSample) { if (newSample == null) { return; } else if (newSample.getValue() instanceof EmptySample) { FXyzSample selectedSample = newSample.getValue(); Project selectedProject = projectsMap.get(selectedSample.getSampleName()); System.out.println(selectedProject); if (selectedProject != null) { changeToWelcomePage(selectedProject.getWelcomePage()); } return; } selectedSample = newSample.getValue(); changeContent(); } }); List<TreeItem<FXyzSample>> projects = contentTree.getRoot().getChildren(); if (!projects.isEmpty()) { TreeItem<FXyzSample> firstProject = projects.get(0); contentTree.getSelectionModel().select(firstProject); } else { changeToWelcomePage(null); } parentProperty().addListener(l -> { if (getParent() != null) { getParent().requestLayout(); } }); setShowMenuPane(false); setShowBottomPane(false); } protected final void buildProjectTree(String searchText) { // rebuild the whole tree (it isn't memory intensive - we only scan // classes once at startup) root = new TreeItem<>(new EmptySample("FXyz-Sampler")); root.setExpanded(true); for (String projectName : projectsMap.keySet()) { final Project project = projectsMap.get(projectName); if (project == null) { continue; } // now work through the project sample tree building the rest SampleTree.TreeNode n = project.getSampleTree().getRoot(); root.getChildren().add(n.createTreeItem()); } // with this newly built and full tree, we filter based on the search text if (searchText != null) { pruneSampleTree(root, searchText); // FIXME weird bug in TreeView I think contentTree.setRoot(null); contentTree.setRoot(root); } // and finally we sort the display a little sort(root, (o1, o2) -> o1.getValue().getSampleName().compareTo(o2.getValue().getSampleName())); } private void sort(TreeItem<FXyzSample> node, Comparator<TreeItem<FXyzSample>> comparator) { node.getChildren().sort(comparator); for (TreeItem<FXyzSample> child : node.getChildren()) { sort(child, comparator); } } // true == keep, false == delete private boolean pruneSampleTree(TreeItem<FXyzSample> treeItem, String searchText) { // we go all the way down to the leaf nodes, and check if they match // the search text. If they do, they stay. If they don't, we remove them. // As we pop back up we check if the branch nodes still have children, // and if not we remove them too if (searchText == null) { return true; } if (treeItem.isLeaf()) { // check for match. Return true if we match (to keep), and false // to delete return treeItem.getValue().getSampleName().toUpperCase().contains(searchText.toUpperCase()); } else { // go down the tree... List<TreeItem<FXyzSample>> toRemove = new ArrayList<>(); for (TreeItem<FXyzSample> child : treeItem.getChildren()) { boolean keep = pruneSampleTree(child, searchText); if (!keep) { toRemove.add(child); } } // remove the unrelated tree items treeItem.getChildren().removeAll(toRemove); // return true if there are children to this branch, false otherwise // (by returning false we say that we should delete this now-empty branch) return !treeItem.getChildren().isEmpty(); } } public String getSearchString() { return searchBar.getText(); } private void changeToWelcomePage(WelcomePage wPage) { //change to index above 0 -> 0 will be content header overlay center.getChildren().clear(); if (null == wPage) { wPage = getDefaultWelcomePage(); } center.getChildren().addAll(wPage.getContent()); } private WelcomePage getDefaultWelcomePage() { // line 1 Label welcomeLabel1 = new Label("Welcome to FXSampler!"); welcomeLabel1.setStyle("-fx-font-size: 2em; -fx-padding: 0 0 0 5;"); // line 2 Label welcomeLabel2 = new Label( "Explore the available UI controls and other interesting projects " + "by clicking on the options to the left."); welcomeLabel2.setStyle("-fx-font-size: 1.25em; -fx-padding: 0 0 0 5;"); WelcomePage wPage = new WelcomePage("Welcome!", new VBox(5, welcomeLabel1, welcomeLabel2)); return wPage; } protected void changeContent() { if (selectedSample == null) { return; } right.getChildren().clear(); if (!center.getChildren().isEmpty()) { center.getChildren().clear(); } updateContent(); } private void updateContent() { center.getChildren().addAll(buildSampleTabContent(selectedSample)); // below add labels / textflow if needed preferably befor controls Node controls = selectedSample.getControlPanel(); if (controls != null) { VBox.setVgrow(controls, Priority.ALWAYS); right.getChildren().addAll(controls); } } private Node buildSampleTabContent(FXyzSample sample) { return FXyzSampleBase.buildSample(sample, stage); } public Map<String, Project> getProjectsMap() { return projectsMap; } public FXyzSample getSelectedSample() { return selectedSample; } }