REVERT ME Remove crumbbar to prep for PR

This commit is contained in:
Clément Fournier 2019-01-28 23:44:20 +01:00
parent 75da199b06
commit 2c357fe941
6 changed files with 9 additions and 331 deletions

View File

@ -16,9 +16,6 @@
<maven.compiler.source>1.${java.version}</maven.compiler.source>
<maven.compiler.target>1.${java.version}</maven.compiler.target>
<build.mr.java9.output>${project.build.outputDirectory}/META-INF/versions/9</build.mr.java9.output>
<openjfx.version>11</openjfx.version>
</properties>
@ -64,57 +61,6 @@
</rulesets>
</configuration>
</plugin>
<!-- Unpack Java 8 specific dependencies into the root output dir -->
<!-- Code specific to higher versions is unpacked into META-INF/versions/n -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>layout-java-8-specific-code</id>
<phase>generate-sources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifact>org.controlsfx:controlsfx:8.40.13</artifact>
<outputDirectory>${project.build.outputDirectory}</outputDirectory>
<includes>**/*</includes>
<excludes>MANIFEST.MF</excludes>
</configuration>
</execution>
<execution>
<id>layout-java-9-specific-code</id>
<phase>generate-resources</phase>
<goals>
<goal>unpack</goal>
</goals>
<configuration>
<artifact>org.controlsfx:controlsfx:9.0.0</artifact>
<outputDirectory>${build.mr.java9.output}</outputDirectory>
<includes>**/*</includes>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<!-- Important for it to work on JRE 9+ -->
<Multi-Release>true</Multi-Release>
<Main-Class>net.sourceforge.pmd.util.fxdesigner.DesignerStarter</Main-Class>
</manifestEntries>
</archive>
<finalName>pmd-ui-${project.version}</finalName>
</configuration>
</plugin>
</plugins>
</build>
@ -169,12 +115,9 @@
<version>0.9.2</version>
</dependency>
<dependency>
<!-- That one is "provided" because several versions are unpacked into versioned dirs -->
<!-- to create a multi-release jar. -->
<groupId>org.controlsfx</groupId>
<artifactId>controlsfx</artifactId>
<version>8.40.13</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>

View File

@ -29,13 +29,13 @@ import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
import net.sourceforge.pmd.util.ClasspathClassLoader;
import net.sourceforge.pmd.util.fxdesigner.app.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.app.CompositeSelectionSource;
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
import net.sourceforge.pmd.util.fxdesigner.model.ASTManager;
import net.sourceforge.pmd.util.fxdesigner.model.ParseAbortedException;
import net.sourceforge.pmd.util.fxdesigner.popups.AuxclasspathSetupController;
import net.sourceforge.pmd.util.fxdesigner.app.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.app.CompositeSelectionSource;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
import net.sourceforge.pmd.util.fxdesigner.util.TextAwareNodeWrapper;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
import net.sourceforge.pmd.util.fxdesigner.util.codearea.AvailableSyntaxHighlighters;
@ -43,7 +43,6 @@ import net.sourceforge.pmd.util.fxdesigner.util.codearea.HighlightLayerCodeArea;
import net.sourceforge.pmd.util.fxdesigner.util.codearea.HighlightLayerCodeArea.LayerId;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ASTTreeItem;
import net.sourceforge.pmd.util.fxdesigner.util.controls.AstTreeView;
import net.sourceforge.pmd.util.fxdesigner.util.controls.NodeParentageCrumbBar;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ToolbarTitledPane;
import javafx.application.Platform;
@ -76,8 +75,6 @@ public class SourceEditorController extends AbstractController<MainDesignerContr
private AstTreeView astTreeView;
@FXML
private HighlightLayerCodeArea<StyleLayerIds> codeEditorArea;
@FXML
private NodeParentageCrumbBar focusNodeParentageCrumbBar;
private final ASTManager astManager;
@ -107,7 +104,6 @@ public class SourceEditorController extends AbstractController<MainDesignerContr
protected void beforeParentInit() {
astTreeView.setDesignerRoot(getDesignerRoot());
focusNodeParentageCrumbBar.setDesignerRoot(getDesignerRoot());
initializeLanguageSelector(); // languageVersionProperty() must be initialized
@ -193,7 +189,7 @@ public class SourceEditorController extends AbstractController<MainDesignerContr
@Override
public ObservableSet<? extends NodeSelectionSource> getSubSelectionSources() {
return FXCollections.observableSet(astTreeView, focusNodeParentageCrumbBar);
return FXCollections.observableSet(astTreeView);
}

View File

@ -10,14 +10,14 @@ import org.reactfx.EventStream;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.fxdesigner.MainDesignerController;
import net.sourceforge.pmd.util.fxdesigner.XPathPanelController;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
import net.sourceforge.pmd.util.fxdesigner.util.controls.AstTreeView;
import net.sourceforge.pmd.util.fxdesigner.util.controls.NodeParentageCrumbBar;
/**
* A control or controller that somehow displays nodes in a form that the user can select.
* When a node is selected by the user (e.g. {@link AstTreeView}, {@link NodeParentageCrumbBar}, etc),
* When a node is selected by the user (e.g. {@link AstTreeView}, {@link XPathPanelController}, etc),
* the whole UI is synchronized to reflect information about the node. This includes scrolling
* the TreeView, the editor, etc. To achieve that uniformly, node selection events are merged
* into a global stream for the whole app. Events from that stream are handled by {@link MainDesignerController}.

View File

@ -1,237 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.util.fxdesigner.util.controls;
import static net.sourceforge.pmd.internal.util.IteratorUtil.asReversed;
import static net.sourceforge.pmd.internal.util.IteratorUtil.count;
import static net.sourceforge.pmd.util.fxdesigner.util.DesignerIteratorUtil.parentIterator;
import java.util.function.Function;
import org.controlsfx.control.BreadCrumbBar;
import org.reactfx.EventSource;
import org.reactfx.EventStream;
import org.reactfx.value.Val;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.fxdesigner.app.DesignerRoot;
import net.sourceforge.pmd.util.fxdesigner.app.NodeSelectionSource;
import javafx.application.Platform;
import javafx.css.PseudoClass;
import javafx.scene.control.Button;
import javafx.scene.control.Labeled;
import javafx.scene.control.Tooltip;
import javafx.scene.control.TreeItem;
import javafx.scene.layout.Region;
import javafx.util.Callback;
/**
* Bread crumb bar to display the parents of a node. Avoids overflow
* by estimating the number of crumbs we can layout based on the actual
* size of the control.
*
* @author Clément Fournier
* @since 7.0.0
*/
public class NodeParentageCrumbBar extends BreadCrumbBar<Node> implements NodeSelectionSource {
private static final int DEFAULT_PX_BY_CHAR = 5;
private static final int DEFAULT_CONSTANT_PADDING = 19;
/** Special item used to truncate paths when they're too long. */
private final TreeItem<Node> ellipsisCrumb = new TreeItem<>(null);
/** number of nodes currently behind the ellipsis */
private int numElidedNodes = 0;
private final EventSource<Node> selectionEvents = new EventSource<>();
private DesignerRoot designerRoot;
public NodeParentageCrumbBar() {
// This allows to click on a parent crumb and keep the children crumb
setAutoNavigationEnabled(false);
// captured in the closure
final Callback<TreeItem<Node>, Button> originalCrumbFactory = getCrumbFactory();
setOnCrumbAction(ev -> {
if (ev.getSelectedCrumb() != ellipsisCrumb) {
selectionEvents.push(ev.getSelectedCrumb().getValue());
}
});
setCrumbFactory(item -> {
Button button = originalCrumbFactory.call(item);
if (item == ellipsisCrumb) {
button.setText("... (" + numElidedNodes + ")");
button.setTooltip(new Tooltip(numElidedNodes + " ancestors are not shown"));
}
// we use that to communicate the node later on
button.setUserData(item);
Val.wrap(button.focusedProperty())
.values()
.distinct()
.filter(Boolean::booleanValue)
// will change the node in the treeview on <- -> key presses
.subscribe(b -> getOnCrumbAction().handle(new BreadCrumbActionEvent<>(item)));
return button;
});
}
@Override
public boolean alwaysHandleSelection() {
// We need to reset the pseudo class we artificially added.
// if the event originated from here, then we know the crumb is displayed
// and we won't reset the displayed nodes
return true;
}
@Override
public EventStream<NodeSelectionEvent> getSelectionEvents() {
return selectionEvents.map(n -> new NodeSelectionEvent(n, this));
}
// getSelectedCrumb gets the deepest displayed node
/**
* If the node is already displayed on the crumbbar, only
* sets the focus on it. Otherwise, sets the node to be
* the deepest one of the crumb bar. Noop if node is null.
*/
@Override
public void setFocusNode(Node node) {
if (node == null) {
return;
}
boolean found = false;
// We're trying to estimate the ratio of px/crumb,
// to make an educated guess about how many crumbs we can fit
// in case we need to call setDeepestNode
int totalNumChar = 0;
int totalNumCrumbs = 0;
// the sum of children width is the actual width with overflow
// the width of this control is the max acceptable width *without* overflow
double totalChildrenWidth = 0;
// constant padding around the graphic of a BreadCrumbButton
// (difference between width of a BreadCrumbButton and that of its graphic)
double constantPadding = Double.NaN;
for (javafx.scene.Node button : asReversed(getChildren())) {
Node n = (Node) ((TreeItem<?>) button.getUserData()).getValue();
// set the focus on the one being selected, remove on the others
// calling requestFocus would switch the focus from eg the treeview to the crumb bar (unusable)
button.pseudoClassStateChanged(PseudoClass.getPseudoClass("focused"), node.equals(n));
// update counters
totalNumChar += ((Labeled) button).getText().length();
double childWidth = ((Region) button).getWidth();
totalChildrenWidth += childWidth;
totalNumCrumbs++;
if (Double.isNaN(constantPadding)) {
Region graphic = (Region) ((Labeled) button).getGraphic();
if (graphic != null) {
constantPadding = childWidth - graphic.getWidth();
}
}
if (node.equals(n)) {
found = true;
}
}
if (!found) {
// Then we reset the deepest node.
setDeepestNode(node, getWidthEstimator(totalNumChar, totalChildrenWidth, totalNumCrumbs, constantPadding));
// set the deepest as focused
Platform.runLater(() ->
getChildren()
.get(getChildren().size() - 1)
.pseudoClassStateChanged(PseudoClass.getPseudoClass("focused"), true)
);
}
}
/**
* Sets the given node to the selected (deepest) crumb. Parent crumbs are
* added until they are estimated to overflow the visual space, after
* which they are hidden into the ellipsis crumb.
*
* @param node Node to set
* @param widthEstimator Estimates the visual width of the crumb for one node
*/
private void setDeepestNode(Node node, Function<Node, Double> widthEstimator) {
TreeItem<Node> deepest = new TreeItem<>(node);
TreeItem<Node> current = deepest;
Node parent = node.jjtGetParent();
double pathLength = widthEstimator.apply(node);
final double maxPathLength = getWidth() * 0.9;
while (parent != null && pathLength < maxPathLength) {
TreeItem<Node> newItem = new TreeItem<>(parent);
newItem.getChildren().add(current);
current = newItem;
pathLength += widthEstimator.apply(parent);
parent = current.getValue().jjtGetParent();
}
if (pathLength >= maxPathLength
// if parent == null then it's the root, no need for ellipsis
&& parent != null) {
numElidedNodes = count(parentIterator(parent, true));
// the rest are children of the ellipsis
ellipsisCrumb.getChildren().clear();
ellipsisCrumb.getChildren().add(current);
}
setSelectedCrumb(deepest);
}
private Function<Node, Double> getWidthEstimator(int totalNumDisplayedChars, double totalChildrenWidth, int totalNumCrumbs, double constantPadding) {
double safeConstantPadding = Double.isNaN(constantPadding)
? DEFAULT_CONSTANT_PADDING // that's the value on my machine
: constantPadding;
double thisPxByChar = totalNumDisplayedChars == 0
? DEFAULT_PX_BY_CHAR // we have no data, too bad
: (totalChildrenWidth - safeConstantPadding * totalNumCrumbs) / totalNumDisplayedChars;
return node -> node.getXPathNodeName().length() * (thisPxByChar + 1 /*scale it up a bit*/) + safeConstantPadding;
}
@Override
public DesignerRoot getDesignerRoot() {
return designerRoot;
}
public void setDesignerRoot(DesignerRoot designerRoot) {
this.designerRoot = designerRoot;
}
@Override
public String getDebugName() {
return "crumb-bar";
}
}

View File

@ -6,7 +6,6 @@
<?import org.kordamp.ikonli.javafx.FontIcon?>
<?import net.sourceforge.pmd.util.fxdesigner.util.codearea.HighlightLayerCodeArea?>
<?import net.sourceforge.pmd.util.fxdesigner.util.controls.AstTreeView?>
<?import net.sourceforge.pmd.util.fxdesigner.util.controls.NodeParentageCrumbBar?>
<?import net.sourceforge.pmd.util.fxdesigner.util.controls.ToolbarTitledPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.MenuButton?>
@ -80,8 +79,8 @@
</SplitPane>
</center>
<bottom>
<NodeParentageCrumbBar fx:id="focusNodeParentageCrumbBar"
stylesheets="@../css/crumbbar.css"
prefHeight="30" id="main-toolbar" />
<!--<NodeParentageCrumbBar fx:id="focusNodeParentageCrumbBar"-->
<!--stylesheets="@../css/crumbbar.css"-->
<!--prefHeight="30" id="main-toolbar" />-->
</bottom>
</BorderPane>

View File

@ -1,23 +0,0 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
/*
* Sheet for the AST crumb bar.
*/
@import "constants";
.bread-crumb-bar {
-fx-border-color: transparent;
.button {
-fx-background-color: @fx-base;
-fx-border-color: @darker-accent;
-fx-border-style: none solid none solid;
-fx-border-width: 1;
}
.button:focused {
-fx-background-color: @selection-focus-color;
}
}