Merge branch 'master' into designer-exception-noise

This commit is contained in:
Clément Fournier
2019-01-14 22:07:25 +01:00
32 changed files with 705 additions and 261 deletions

View File

@@ -112,7 +112,7 @@
<dependency>
<groupId>org.fxmisc.richtext</groupId>
<artifactId>richtextfx</artifactId>
<version>0.9.0</version>
<version>0.9.2</version>
</dependency>
<dependency>
<groupId>org.controlsfx</groupId>
@@ -133,7 +133,7 @@
</dependency>
<dependency>
<groupId>org.kordamp.ikonli</groupId>
<artifactId>ikonli-fontawesome-pack</artifactId>
<artifactId>ikonli-fontawesome5-pack</artifactId>
<version>2.3.0</version>
</dependency>

View File

@@ -9,7 +9,6 @@ import static net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category.PARSE_
import static net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category.XPATH_EVALUATION_EXCEPTION;
import static net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category.XPATH_OK;
import java.net.URL;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.time.Duration;
@@ -18,7 +17,6 @@ import java.util.Date;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import org.reactfx.EventStream;
import org.reactfx.EventStreams;
@@ -27,11 +25,11 @@ import org.reactfx.value.Var;
import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.util.fxdesigner.model.LogEntry;
import net.sourceforge.pmd.util.fxdesigner.model.LogEntry.Category;
import net.sourceforge.pmd.util.fxdesigner.util.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.SortType;
@@ -44,7 +42,7 @@ import javafx.scene.control.cell.PropertyValueFactory;
* @author Clément Fournier
* @since 6.0.0
*/
public class EventLogController implements Initializable {
public class EventLogController extends AbstractController {
/**
* Exceptions from XPath evaluation or parsing are never emitted
@@ -76,7 +74,8 @@ public class EventLogController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
protected void beforeParentInit() {
logCategoryColumn.setCellValueFactory(new PropertyValueFactory<>("category"));
logMessageColumn.setCellValueFactory(new PropertyValueFactory<>("message"));
final DateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

View File

@@ -6,7 +6,6 @@ package net.sourceforge.pmd.util.fxdesigner;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
@@ -14,7 +13,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.Stack;
import java.util.stream.Collectors;
@@ -26,10 +24,10 @@ import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.lang.symboltable.NameOccurrence;
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
import net.sourceforge.pmd.util.fxdesigner.util.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
import net.sourceforge.pmd.util.fxdesigner.util.LimitedSizeStack;
import net.sourceforge.pmd.util.fxdesigner.util.TextAwareNodeWrapper;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
@@ -39,10 +37,8 @@ import javafx.animation.Timeline;
import javafx.application.Platform;
import javafx.beans.property.DoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.CustomMenuItem;
import javafx.scene.control.Label;
import javafx.scene.control.Menu;
@@ -70,7 +66,7 @@ import javafx.util.Duration;
* @since 6.0.0
*/
@SuppressWarnings("PMD.UnusedPrivateField")
public class MainDesignerController implements Initializable, SettingsOwner {
public class MainDesignerController extends AbstractController {
/**
* Callback to the owner.
@@ -88,17 +84,9 @@ public class MainDesignerController implements Initializable, SettingsOwner {
@FXML
private Menu openRecentMenu;
@FXML
private MenuItem exportToTestCodeMenuItem;
@FXML
private MenuItem exportXPathMenuItem;
@FXML
private Menu fileMenu;
/* Center toolbar */
@FXML
private ChoiceBox<LanguageVersion> languageChoiceBox;
@FXML
private ChoiceBox<String> xpathVersionChoiceBox;
@FXML
private ToggleButton bottomTabsToggle;
/* Bottom panel */
@FXML
@@ -121,18 +109,14 @@ public class MainDesignerController implements Initializable, SettingsOwner {
// Other fields
private Stack<File> recentFiles = new LimitedSizeStack<>(5);
// Properties
private Val<LanguageVersion> languageVersion = Val.constant(DesignerUtil.defaultLanguageVersion());
private Val<String> xpathVersion = Val.constant(DesignerUtil.defaultXPathVersion());
public MainDesignerController(DesignerRoot owner) {
this.designerRoot = owner;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
protected void beforeParentInit() {
try {
SettingsPersistenceUtil.restoreProperties(this, DesignerUtil.getSettingsFile());
} catch (Exception e) {
@@ -141,55 +125,23 @@ public class MainDesignerController implements Initializable, SettingsOwner {
e.printStackTrace();
}
initializeLanguageVersionMenu();
initializeViewAnimation();
xpathPanelController.initialiseVersionChoiceBox(xpathVersionChoiceBox);
languageVersion = Val.wrap(languageChoiceBox.getSelectionModel().selectedItemProperty());
DesignerUtil.rewire(sourceEditorController.languageVersionProperty(),
languageVersion, this::setLanguageVersion);
xpathVersion = Val.wrap(xpathVersionChoiceBox.getSelectionModel().selectedItemProperty());
DesignerUtil.rewire(xpathPanelController.xpathVersionProperty(),
xpathVersion, this::setXpathVersion);
licenseMenuItem.setOnAction(e -> showLicensePopup());
openFileMenuItem.setOnAction(e -> onOpenFileClicked());
openRecentMenu.setOnAction(e -> updateRecentFilesMenu());
openRecentMenu.setOnShowing(e -> updateRecentFilesMenu());
fileMenu.setOnShowing(e -> onFileMenuShowing());
exportXPathMenuItem.setOnAction(e -> {
try {
xpathPanelController.showExportXPathToRuleWizard();
} catch (IOException e1) {
e1.printStackTrace();
}
});
setupAuxclasspathMenuItem.setOnAction(e -> sourceEditorController.showAuxclasspathSetupPopup(designerRoot));
Platform.runLater(this::updateRecentFilesMenu);
Platform.runLater(this::refreshAST); // initial refreshing
Platform.runLater(() -> sourceEditorController.moveCaret(0, 0));
Platform.runLater(() -> { // fixes choicebox bad rendering on first opening
languageChoiceBox.show();
languageChoiceBox.hide();
});
}
private void initializeLanguageVersionMenu() {
List<LanguageVersion> supported = DesignerUtil.getSupportedLanguageVersions();
supported.sort(LanguageVersion::compareTo);
languageChoiceBox.getItems().addAll(supported);
languageChoiceBox.setConverter(DesignerUtil.languageVersionStringConverter());
languageChoiceBox.getSelectionModel().select(DesignerUtil.defaultLanguageVersion());
languageChoiceBox.show();
@Override
protected void afterChildrenInit() {
updateRecentFilesMenu();
refreshAST(); // initial refreshing
sourceEditorController.moveCaret(0, 0);
}
@@ -360,7 +312,7 @@ public class MainDesignerController implements Initializable, SettingsOwner {
sourceEditorController.setText(source);
LanguageVersion guess = DesignerUtil.getLanguageVersionFromExtension(file.getName());
if (guess != null) { // guess the language from the extension
languageChoiceBox.getSelectionModel().select(guess);
sourceEditorController.setLanguageVersion(guess);
refreshAST();
}
@@ -417,36 +369,17 @@ public class MainDesignerController implements Initializable, SettingsOwner {
public LanguageVersion getLanguageVersion() {
return languageVersion.getValue();
return sourceEditorController.getLanguageVersion();
}
public void setLanguageVersion(LanguageVersion version) {
if (languageChoiceBox.getItems().contains(version)) {
languageChoiceBox.getSelectionModel().select(version);
}
sourceEditorController.setLanguageVersion(version);
}
public Val<LanguageVersion> languageVersionProperty() {
return languageVersion;
}
public String getXpathVersion() {
return xpathVersion.getValue();
}
public void setXpathVersion(String version) {
if (xpathVersionChoiceBox.getItems().contains(version)) {
xpathVersionChoiceBox.getSelectionModel().select(version);
}
}
public Val<String> xpathVersionProperty() {
return xpathVersion;
return sourceEditorController.languageVersionProperty();
}
@@ -496,7 +429,7 @@ public class MainDesignerController implements Initializable, SettingsOwner {
@Override
public List<SettingsOwner> getChildrenSettingsNodes() {
return Arrays.asList(xpathPanelController, sourceEditorController);
public List<AbstractController> getChildren() {
return Arrays.asList(xpathPanelController, sourceEditorController, nodeInfoPanelController, eventLogPanelController);
}
}

View File

@@ -4,11 +4,9 @@
package net.sourceforge.pmd.util.fxdesigner;
import java.net.URL;
import java.util.Collections;
import java.util.Iterator;
import java.util.Objects;
import java.util.ResourceBundle;
import org.reactfx.EventStreams;
@@ -18,13 +16,13 @@ import net.sourceforge.pmd.lang.java.ast.TypeNode;
import net.sourceforge.pmd.lang.symboltable.NameDeclaration;
import net.sourceforge.pmd.util.fxdesigner.model.MetricEvaluator;
import net.sourceforge.pmd.util.fxdesigner.model.MetricResult;
import net.sourceforge.pmd.util.fxdesigner.util.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeCell;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ScopeHierarchyTreeItem;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
@@ -40,7 +38,7 @@ import javafx.scene.control.TreeView;
* @since 6.0.0
*/
@SuppressWarnings("PMD.UnusedPrivateField")
public class NodeInfoPanelController implements Initializable {
public class NodeInfoPanelController extends AbstractController {
private final MainDesignerController parent;
@@ -65,9 +63,8 @@ public class NodeInfoPanelController implements Initializable {
parent = mainController;
}
@Override
public void initialize(URL location, ResourceBundle resources) {
protected void beforeParentInit() {
EventStreams.valuesOf(scopeHierarchyTreeView.getSelectionModel().selectedItemProperty())
.filter(Objects::nonNull)
.map(TreeItem::getValue)

View File

@@ -11,7 +11,6 @@ import static net.sourceforge.pmd.util.fxdesigner.util.IteratorUtil.toIterable;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;
import java.util.Collection;
@@ -19,7 +18,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.ResourceBundle;
import java.util.function.BiConsumer;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
@@ -38,22 +36,26 @@ import net.sourceforge.pmd.util.ClasspathClassLoader;
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.util.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
import net.sourceforge.pmd.util.fxdesigner.util.TextAwareNodeWrapper;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
import net.sourceforge.pmd.util.fxdesigner.util.codearea.AvailableSyntaxHighlighters;
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.ASTTreeCell;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ASTTreeItem;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ToolbarTitledPane;
import net.sourceforge.pmd.util.fxdesigner.util.controls.TreeViewWrapper;
import javafx.application.Platform;
import javafx.css.PseudoClass;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.SelectionModel;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
@@ -64,10 +66,16 @@ import javafx.scene.control.TreeView;
* @author Clément Fournier
* @since 6.0.0
*/
public class SourceEditorController implements Initializable, SettingsOwner {
public class SourceEditorController extends AbstractController {
private static final Duration AST_REFRESH_DELAY = Duration.ofMillis(100);
@FXML
private ToolbarTitledPane editorTitledPane;
@FXML
private MenuButton languageSelectionMenuButton;
@FXML
private Label sourceCodeTitleLabel;
@FXML
private Label astTitleLabel;
@FXML
@@ -93,6 +101,9 @@ public class SourceEditorController implements Initializable, SettingsOwner {
}
});
private Var<LanguageVersion> languageVersionUIProperty;
public SourceEditorController(DesignerRoot owner, MainDesignerController mainController) {
parent = mainController;
astManager = new ASTManager(owner);
@@ -100,16 +111,23 @@ public class SourceEditorController implements Initializable, SettingsOwner {
}
@Override
public void initialize(URL location, ResourceBundle resources) {
protected void beforeParentInit() {
treeViewWrapper = new TreeViewWrapper<>(astTreeView);
astTreeView.setCellFactory(treeView -> new ASTTreeCell(parent));
initializeLanguageSelector(); // languageVersionProperty() must be initialized
languageVersionProperty().values()
.filterMap(Objects::nonNull, LanguageVersion::getLanguage)
.distinct()
.subscribe(this::updateSyntaxHighlighter);
languageVersionProperty().values()
.filter(Objects::nonNull)
.map(LanguageVersion::getShortName)
.map(lang -> "Source Code (" + lang + ")")
.subscribe(editorTitledPane::setTitle);
EventStreams.valuesOf(astTreeView.getSelectionModel().selectedItemProperty())
.filterMap(Objects::nonNull, TreeItem::getValue)
.subscribe(parent::onNodeItemSelected);
@@ -130,6 +148,33 @@ public class SourceEditorController implements Initializable, SettingsOwner {
}
private void initializeLanguageSelector() {
ToggleGroup languageToggleGroup = new ToggleGroup();
DesignerUtil.getSupportedLanguageVersions()
.stream()
.sorted(LanguageVersion::compareTo)
.map(lv -> {
RadioMenuItem item = new RadioMenuItem(lv.getShortName());
item.setUserData(lv);
return item;
})
.forEach(item -> {
languageToggleGroup.getToggles().add(item);
languageSelectionMenuButton.getItems().add(item);
});
languageVersionUIProperty = DesignerUtil.mapToggleGroupToUserData(languageToggleGroup);
}
@Override
protected void afterParentInit() {
DesignerUtil.rewire(astManager.languageVersionProperty(), languageVersionUIProperty);
}
private IntFunction<javafx.scene.Node> lineNumberFactory() {
IntFunction<javafx.scene.Node> base = LineNumberFactory.get(codeEditorArea);
Val<Integer> activePar = Val.wrap(codeEditorArea.currentParagraphProperty());
@@ -341,20 +386,19 @@ public class SourceEditorController implements Initializable, SettingsOwner {
@PersistentProperty
public LanguageVersion getLanguageVersion() {
return astManager.getLanguageVersion();
return languageVersionUIProperty.getValue();
}
public void setLanguageVersion(LanguageVersion version) {
astManager.setLanguageVersion(version);
languageVersionUIProperty.setValue(version);
}
public Var<LanguageVersion> languageVersionProperty() {
return astManager.languageVersionProperty();
return languageVersionUIProperty;
}
/**
* Returns the most up-to-date compilation unit, or empty if it can't be parsed.
*/
@@ -418,5 +462,4 @@ public class SourceEditorController implements Initializable, SettingsOwner {
return styleClass;
}
}
}

View File

@@ -6,12 +6,11 @@ package net.sourceforge.pmd.util.fxdesigner;
import java.io.IOException;
import java.net.URL;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.ResourceBundle;
import java.util.function.Supplier;
import java.util.stream.Collectors;
@@ -33,33 +32,34 @@ import net.sourceforge.pmd.util.fxdesigner.model.ObservableXPathRuleBuilder;
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluationException;
import net.sourceforge.pmd.util.fxdesigner.model.XPathEvaluator;
import net.sourceforge.pmd.util.fxdesigner.popups.ExportXPathWizardController;
import net.sourceforge.pmd.util.fxdesigner.util.AbstractController;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
import net.sourceforge.pmd.util.fxdesigner.util.TextAwareNodeWrapper;
import net.sourceforge.pmd.util.fxdesigner.util.autocomplete.CompletionResultSource;
import net.sourceforge.pmd.util.fxdesigner.util.autocomplete.XPathAutocompleteProvider;
import net.sourceforge.pmd.util.fxdesigner.util.autocomplete.XPathCompletionSource;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
import net.sourceforge.pmd.util.fxdesigner.util.codearea.SyntaxHighlightingCodeArea;
import net.sourceforge.pmd.util.fxdesigner.util.codearea.syntaxhighlighting.XPathSyntaxHighlighter;
import net.sourceforge.pmd.util.fxdesigner.util.controls.PropertyTableView;
import net.sourceforge.pmd.util.fxdesigner.util.controls.ToolbarTitledPane;
import net.sourceforge.pmd.util.fxdesigner.util.controls.XpathViolationListCell;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuButton;
import javafx.scene.control.MenuItem;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToggleGroup;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.stage.Modality;
@@ -68,13 +68,14 @@ import javafx.stage.StageStyle;
/**
* XPath panel controller.
* XPath panel controller. One such controller is a presenter for an {@link ObservableXPathRuleBuilder},
* which stores all data about one currently edited rule.
*
* @author Clément Fournier
* @see ExportXPathWizardController
* @since 6.0.0
*/
public class XPathPanelController implements Initializable, SettingsOwner {
public class XPathPanelController extends AbstractController {
private static final Duration XPATH_REFRESH_DELAY = Duration.ofMillis(100);
private final DesignerRoot designerRoot;
@@ -82,6 +83,13 @@ public class XPathPanelController implements Initializable, SettingsOwner {
private final XPathEvaluator xpathEvaluator = new XPathEvaluator();
private final ObservableXPathRuleBuilder ruleBuilder = new ObservableXPathRuleBuilder();
@FXML
public ToolbarTitledPane expressionTitledPane;
@FXML
public Button exportXpathToRuleButton;
@FXML
private MenuButton xpathVersionMenuButton;
@FXML
private PropertyTableView propertyTableView;
@FXML
@@ -91,9 +99,8 @@ public class XPathPanelController implements Initializable, SettingsOwner {
@FXML
private ListView<TextAwareNodeWrapper> xpathResultListView;
// Actually a child of the main view toolbar, but this controller is responsible for it
@SuppressWarnings("PMD.SingularField")
private ChoiceBox<String> xpathVersionChoiceBox;
// ui property
private Var<String> xpathVersionUIProperty = Var.newSimpleVar(XPathRuleQuery.XPATH_2_0);
public XPathPanelController(DesignerRoot owner, MainDesignerController mainController) {
@@ -105,21 +112,24 @@ public class XPathPanelController implements Initializable, SettingsOwner {
@Override
public void initialize(URL location, ResourceBundle resources) {
protected void beforeParentInit() {
xpathExpressionArea.setSyntaxHighlighter(new XPathSyntaxHighlighter());
initGenerateXPathFromStackTrace();
initialiseVersionSelection();
expressionTitledPane.titleProperty().bind(xpathVersionUIProperty.map(v -> "XPath Expression (" + v + ")"));
xpathResultListView.setCellFactory(v -> new XpathViolationListCell());
exportXpathToRuleButton.setOnAction(e -> showExportXPathToRuleWizard());
EventStreams.valuesOf(xpathResultListView.getSelectionModel().selectedItemProperty())
.conditionOn(xpathResultListView.focusedProperty())
.filter(Objects::nonNull)
.map(TextAwareNodeWrapper::getNode)
.subscribe(parent::onNodeItemSelected);
Platform.runLater(this::bindToParent);
xpathExpressionArea.richChanges()
.filter(t -> !t.isIdentity())
.successionEnds(XPATH_REFRESH_DELAY)
@@ -127,12 +137,51 @@ public class XPathPanelController implements Initializable, SettingsOwner {
.or(xpathVersionProperty().changes())
.subscribe(tick -> parent.refreshXPathResults());
Supplier<CompletionResultSource> suggestionMaker = () -> XPathCompletionSource.forLanguage(parent.getLanguageVersion().getLanguage());
}
@Override
protected void afterParentInit() {
bindToParent();
// init autocompletion only after binding to parent and settings restore
// otherwise the popup is shown on startup
Supplier<CompletionResultSource> suggestionMaker = () -> XPathCompletionSource.forLanguage(parent.getLanguageVersion().getLanguage());
new XPathAutocompleteProvider(xpathExpressionArea, suggestionMaker).initialiseAutoCompletion();
}
// Binds the underlying rule parameters to the parent UI, disconnecting it from the wizard if need be
private void bindToParent() {
DesignerUtil.rewire(getRuleBuilder().languageProperty(), Val.map(parent.languageVersionProperty(), LanguageVersion::getLanguage));
DesignerUtil.rewireInit(getRuleBuilder().xpathVersionProperty(), xpathVersionProperty());
DesignerUtil.rewireInit(getRuleBuilder().xpathExpressionProperty(), xpathExpressionProperty());
DesignerUtil.rewireInit(getRuleBuilder().rulePropertiesProperty(),
propertyTableView.rulePropertiesProperty(), propertyTableView::setRuleProperties);
}
private void initialiseVersionSelection() {
ToggleGroup xpathVersionToggleGroup = new ToggleGroup();
List<String> versionItems = new ArrayList<>();
versionItems.add(XPathRuleQuery.XPATH_1_0);
versionItems.add(XPathRuleQuery.XPATH_1_0_COMPATIBILITY);
versionItems.add(XPathRuleQuery.XPATH_2_0);
versionItems.forEach(v -> {
RadioMenuItem item = new RadioMenuItem("XPath " + v);
item.setUserData(v);
item.setToggleGroup(xpathVersionToggleGroup);
xpathVersionMenuButton.getItems().add(item);
});
xpathVersionUIProperty = DesignerUtil.mapToggleGroupToUserData(xpathVersionToggleGroup);
setXpathVersion(XPathRuleQuery.XPATH_2_0);
}
private void initGenerateXPathFromStackTrace() {
@@ -178,30 +227,6 @@ public class XPathPanelController implements Initializable, SettingsOwner {
}
// Binds the underlying rule parameters to the parent UI, disconnecting it from the wizard if need be
private void bindToParent() {
DesignerUtil.rewire(getRuleBuilder().languageProperty(),
Val.map(parent.languageVersionProperty(), LanguageVersion::getLanguage));
DesignerUtil.rewire(getRuleBuilder().xpathVersionProperty(), parent.xpathVersionProperty());
DesignerUtil.rewire(getRuleBuilder().xpathExpressionProperty(), xpathExpressionProperty());
DesignerUtil.rewire(getRuleBuilder().rulePropertiesProperty(),
propertyTableView.rulePropertiesProperty(), propertyTableView::setRuleProperties);
}
public void initialiseVersionChoiceBox(ChoiceBox<String> choiceBox) {
this.xpathVersionChoiceBox = choiceBox;
ObservableList<String> versionItems = choiceBox.getItems();
versionItems.add(XPathRuleQuery.XPATH_1_0);
versionItems.add(XPathRuleQuery.XPATH_1_0_COMPATIBILITY);
versionItems.add(XPathRuleQuery.XPATH_2_0);
xpathVersionChoiceBox.getSelectionModel().select(XPathRuleQuery.XPATH_2_0);
choiceBox.setConverter(DesignerUtil.stringConverter(s -> "XPath " + s, s -> s.substring(6)));
}
/**
@@ -223,11 +248,11 @@ public class XPathPanelController implements Initializable, SettingsOwner {
}
ObservableList<Node> results
= FXCollections.observableArrayList(xpathEvaluator.evaluateQuery(compilationUnit,
version,
getXpathVersion(),
xpath,
ruleBuilder.getRuleProperties()));
= FXCollections.observableArrayList(xpathEvaluator.evaluateQuery(compilationUnit,
version,
getXpathVersion(),
xpath,
ruleBuilder.getRuleProperties()));
xpathResultListView.setItems(results.stream().map(parent::wrapNode).collect(Collectors.toCollection(LiveArrayList::new)));
parent.highlightXPathResults(results);
violationsTitledPane.setText("Matched nodes\t(" + results.size() + ")");
@@ -256,9 +281,9 @@ public class XPathPanelController implements Initializable, SettingsOwner {
}
public void showExportXPathToRuleWizard() throws IOException {
public void showExportXPathToRuleWizard() {
ExportXPathWizardController wizard
= new ExportXPathWizardController(xpathExpressionProperty());
= new ExportXPathWizardController(xpathExpressionProperty());
FXMLLoader loader = new FXMLLoader(getClass().getResource("fxml/xpath-export-wizard.fxml"));
loader.setController(wizard);
@@ -268,7 +293,12 @@ public class XPathPanelController implements Initializable, SettingsOwner {
dialog.setOnCloseRequest(e -> wizard.shutdown());
dialog.initModality(Modality.WINDOW_MODAL);
Parent root = loader.load();
Parent root;
try {
root = loader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
Scene scene = new Scene(root);
//stage.setTitle("PMD Rule Designer (v " + PMD.VERSION + ')');
dialog.setScene(scene);
@@ -276,7 +306,6 @@ public class XPathPanelController implements Initializable, SettingsOwner {
}
@PersistentProperty
public String getXpathExpression() {
return xpathExpressionArea.getText();
}
@@ -287,24 +316,23 @@ public class XPathPanelController implements Initializable, SettingsOwner {
}
public Val<String> xpathExpressionProperty() {
return Val.wrap(xpathExpressionArea.textProperty());
public Var<String> xpathExpressionProperty() {
return Var.fromVal(xpathExpressionArea.textProperty(), this::setXpathExpression);
}
@PersistentProperty
public String getXpathVersion() {
return getRuleBuilder().getXpathVersion();
return xpathVersionProperty().getValue();
}
public void setXpathVersion(String xpathVersion) {
getRuleBuilder().setXpathVersion(xpathVersion);
xpathVersionProperty().setValue(xpathVersion);
}
public Var<String> xpathVersionProperty() {
return getRuleBuilder().xpathVersionProperty();
return xpathVersionUIProperty;
}

View File

@@ -7,6 +7,7 @@ package net.sourceforge.pmd.util.fxdesigner.model;
import org.reactfx.value.Var;
import net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsPersistenceUtil.PersistentProperty;
/**
@@ -22,6 +23,7 @@ public class ObservableXPathRuleBuilder extends ObservableRuleBuilder {
private final Var<String> xpathExpression = Var.newSimpleVar("");
@PersistentProperty
public String getXpathVersion() {
return xpathVersion.getValue();
}
@@ -37,11 +39,17 @@ public class ObservableXPathRuleBuilder extends ObservableRuleBuilder {
}
@PersistentProperty
public String getXpathExpression() {
return xpathExpression.getValue();
}
public void setXpathExpression(String value) {
xpathExpression.setValue(value);
}
public Var<String> xpathExpressionProperty() {
return xpathExpression;
}

View File

@@ -6,7 +6,6 @@ package net.sourceforge.pmd.util.fxdesigner.popups;
import static net.sourceforge.pmd.properties.MultiValuePropertyDescriptor.DEFAULT_DELIMITER;
import static net.sourceforge.pmd.properties.MultiValuePropertyDescriptor.DEFAULT_NUMERIC_DELIMITER;
import static net.sourceforge.pmd.util.fxdesigner.util.DesignerUtil.rewire;
import java.net.URL;
import java.util.Objects;
@@ -127,10 +126,10 @@ public class EditPropertyDialogController implements Initializable {
public void bindToDescriptor(PropertyDescriptorSpec spec, ObservableList<PropertyDescriptorSpec> allDescriptors) {
backingDescriptor.setValue(spec);
backingDescriptorList.setValue(allDescriptors);
rewire(spec.nameProperty(), this.nameProperty(), this::setName);
rewire(spec.typeIdProperty(), this.typeIdProperty(), this::setTypeId);
rewire(spec.valueProperty(), this.valueProperty(), this::setValue);
rewire(spec.descriptionProperty(), this.descriptionProperty(), this::setDescription);
DesignerUtil.rewireInit(spec.nameProperty(), this.nameProperty(), this::setName);
DesignerUtil.rewireInit(spec.typeIdProperty(), this.typeIdProperty(), this::setTypeId);
DesignerUtil.rewireInit(spec.valueProperty(), this.valueProperty(), this::setValue);
DesignerUtil.rewireInit(spec.descriptionProperty(), this.descriptionProperty(), this::setDescription);
}

View File

@@ -0,0 +1,82 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.util.fxdesigner.util;
import java.net.URL;
import java.util.Collections;
import java.util.List;
import java.util.ResourceBundle;
import net.sourceforge.pmd.util.fxdesigner.util.beans.SettingsOwner;
import javafx.application.Platform;
import javafx.fxml.Initializable;
/**
* Make the initialization cycle of JavaFX clearer. Children controller
* are initialized before their parent, but sometimes it should only
* perform some actions after its parent has been initialized, e.g. binding
* properties that depend on a restored setting or stuff. This is part
* of the reason why {@link Platform#runLater(Runnable)} can sometimes
* be enough to solve initialization problems.
*
* This only works if all controllers in the tree extend this class.
*
* @author Clément Fournier
* @since 7.0.0
*/
public class AbstractController implements Initializable, SettingsOwner {
@Override
public final void initialize(URL url, ResourceBundle resourceBundle) {
beforeParentInit();
for (AbstractController child : getChildren()) {
child.afterParentInit();
}
afterChildrenInit();
}
/**
* Executed before the parent's initialization.
* Always executed once at the start of the initialization
* of this controller.
*/
protected void beforeParentInit() {
// by default do nothing
}
/**
* Executed after the parent's initialization (so after {@link #afterChildrenInit()}).
* This also means, after persistent settings restoration. If this node has no
* parent, then this is never executed.
*/
protected void afterParentInit() {
// by default do nothing
}
/**
* Runs once after every child has finished their initialization.
* This will be run in all cases. It's only useful if the children
* do something useful in their {@link #afterParentInit()}.
*/
protected void afterChildrenInit() {
// by default do nothing
}
@Override
public List<? extends SettingsOwner> getChildrenSettingsNodes() {
return getChildren();
}
protected List<? extends AbstractController> getChildren() {
return Collections.emptyList();
}
}

View File

@@ -23,6 +23,7 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.reactfx.value.Var;
import net.sourceforge.pmd.lang.Language;
import net.sourceforge.pmd.lang.LanguageRegistry;
@@ -30,10 +31,12 @@ import net.sourceforge.pmd.lang.LanguageVersion;
import net.sourceforge.pmd.lang.Parser;
import net.sourceforge.pmd.lang.rule.xpath.XPathRuleQuery;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.Property;
import javafx.beans.value.ObservableValue;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip;
import javafx.util.Callback;
import javafx.util.StringConverter;
@@ -126,6 +129,24 @@ public final class DesignerUtil {
}
/**
* Given a toggle group whose toggles all have user data of type T,
* maps the selected toggle property to a Var&lt;T>
*/
@SuppressWarnings("unchecked")
public static <T> Var<T> mapToggleGroupToUserData(ToggleGroup toggleGroup) {
return Var.fromVal(toggleGroup.selectedToggleProperty(), toggleGroup::selectToggle)
.mapBidirectional(
item -> (T) item.getUserData(),
t -> toggleGroup.getToggles()
.stream()
.filter(toggle -> toggle.getUserData().equals(t))
.findFirst()
.orElseThrow(() -> new IllegalStateException("Unknown toggle " + t))
);
}
public static StringConverter<LanguageVersion> languageVersionStringConverter() {
return DesignerUtil.stringConverter(LanguageVersion::getShortName,
s -> LanguageRegistry.findLanguageVersionByTerseName(s.toLowerCase(Locale.ROOT)));
@@ -172,20 +193,27 @@ public final class DesignerUtil {
}
/** Like the other overload, using the setter of the ui property. */
public static <T> void rewireInit(Property<T> underlying, Property<T> ui) {
rewireInit(underlying, ui, ui::setValue);
}
/**
* Binds the underlying property to a source of values. The source property is also initialised using the setter.
* Binds the underlying property to a source of values (UI property). The UI
* property is also initialised using a setter.
*
* @param underlying The underlying property
* @param ui The property exposed to the user (the one in this wizard)
* @param setter Setter to initialise the UI value
* @param <T> Type of values
*/
public static <T> void rewire(Property<T> underlying, ObservableValue<? extends T> ui, Consumer<? super T> setter) {
public static <T> void rewireInit(Property<T> underlying, ObservableValue<? extends T> ui, Consumer<? super T> setter) {
setter.accept(underlying.getValue());
rewire(underlying, ui);
}
/** Like rewire, with no initialisation. */
/** Like rewireInit, with no initialisation. */
public static <T> void rewire(Property<T> underlying, ObservableValue<? extends T> source) {
underlying.unbind();
underlying.bind(source); // Bindings are garbage collected after the popup dies
@@ -230,4 +258,9 @@ public final class DesignerUtil {
public static Optional<String> stackTraceToXPath(Throwable e) {
return stackTraceToXPath(ExceptionUtils.getStackTrace(e));
}
public static Var<Boolean> booleanVar(BooleanProperty p) {
return Var.mapBidirectional(p, Boolean::booleanValue, Function.identity());
}
}

View File

@@ -65,7 +65,10 @@ public class RestorePropertyVisitor extends BeanNodeVisitor<SettingsOwner> {
}
for (SettingsOwner child : target.getChildrenSettingsNodes()) {
model.getChildrenByType().get(child.getClass()).accept(this, child);
BeanModelNode childModel = model.getChildrenByType().get(child.getClass());
if (childModel != null) {
childModel.accept(this, child);
}
}
}

View File

@@ -21,7 +21,7 @@ public interface SettingsOwner {
/** Gets the children of this node in order. */
default List<SettingsOwner> getChildrenSettingsNodes() {
default List<? extends SettingsOwner> getChildrenSettingsNodes() {
return Collections.emptyList();
}

View File

@@ -0,0 +1,97 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.util.fxdesigner.util.controls;
import java.util.Collection;
import java.util.Objects;
import org.reactfx.value.Val;
import org.reactfx.value.Var;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import javafx.scene.control.ToolBar;
import javafx.scene.layout.StackPane;
/**
* A Titled pane that has a toolbar in its header region.
* Supported by some CSS in designer.less.
*
* @author Clément Fournier
* @since 7.0.0
*/
public final class ToolbarTitledPane extends TitledPane {
private ToolBar toolBar = new ToolBar();
private Var<String> title = Var.newSimpleVar("Title");
public ToolbarTitledPane() {
getStyleClass().add("tool-bar-title");
toolBar.setPadding(Insets.EMPTY);
Label titleLabel = new Label("Title");
titleLabel.textProperty().bind(title);
toolBar.getItems().add(titleLabel);
setGraphic(toolBar);
// should be an empty string, binding prevents to set it
textProperty().bind(Val.constant(""));
// The toolbar is too large for the title region and is not
// centered unless we bind the height, like follows
Val.wrap(toolBar.parentProperty())
.values()
.filter(Objects::nonNull)
.subscribe(parent -> {
// The title region is provided by the skin,
// this is the only way to access it outside of css
StackPane titleRegion = (StackPane) parent;
toolBar.maxHeightProperty().unbind();
toolBar.maxHeightProperty().bind(titleRegion.heightProperty());
toolBar.minHeightProperty().unbind();
toolBar.minHeightProperty().bind(titleRegion.heightProperty());
toolBar.prefHeightProperty().unbind();
toolBar.prefHeightProperty().bind(titleRegion.heightProperty());
});
}
public ObservableList<Node> getToolbarItems() {
return toolBar.getItems();
}
public void setToolbarItems(Collection<? extends Node> nodes) {
toolBar.getItems().setAll(nodes);
}
public String getTitle() {
return title.getValue();
}
public void setTitle(String title) {
this.title.setValue(title);
}
/** Title of the pane, not equivalent to {@link #textProperty()}. */
public Var<String> titleProperty() {
return title;
}
}

View File

@@ -32,7 +32,7 @@
styleClass="icon-button"
textAlignment="CENTER">
<graphic>
<FontIcon iconLiteral="fa-plus"/>
<FontIcon iconLiteral="fas-plus"/>
</graphic>
<tooltip>
<Tooltip text="Add new jars"/>
@@ -44,7 +44,7 @@
styleClass="icon-button"
textAlignment="CENTER">
<graphic>
<FontIcon iconLiteral="fa-minus"/>
<FontIcon iconLiteral="fas-minus"/>
</graphic>
<tooltip>
<Tooltip text="Remove item"/>
@@ -52,12 +52,12 @@
</Button>
<Button fx:id="moveItemUpButton" minWidth="-Infinity" mnemonicParsing="false" styleClass="icon-button">
<graphic>
<FontIcon iconLiteral="fa-arrow-up"/>
<FontIcon iconLiteral="fas-arrow-up"/>
</graphic>
</Button>
<Button fx:id="moveItemDownButton" minWidth="-Infinity" mnemonicParsing="false" styleClass="icon-button">
<graphic>
<FontIcon iconLiteral="fa-arrow-down"/>
<FontIcon iconLiteral="fas-arrow-down"/>
</graphic>
<tooltip>
<Tooltip text="Move item down"/>

View File

@@ -18,39 +18,34 @@
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Pane?>
<AnchorPane prefHeight="750.0" prefWidth="1200.0" stylesheets="@../css/designer.css"
xmlns="http://javafx.com/javafx/8.0.121" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="net.sourceforge.pmd.util.fxdesigner.MainDesignerController">
<AnchorPane prefHeight="750.0" prefWidth="1200.0" stylesheets="@../css/designer.css" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="net.sourceforge.pmd.util.fxdesigner.MainDesignerController">
<children>
<BorderPane prefHeight="600.0" prefWidth="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<top>
<AnchorPane>
<children>
<MenuBar layoutX="-11.0" maxHeight="20.0" prefHeight="20.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="-11.0" AnchorPane.rightAnchor="11.0" AnchorPane.topAnchor="0.0">
<MenuBar layoutX="-11.0" maxHeight="20.0" prefHeight="20.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="-11.0" AnchorPane.rightAnchor="11.0" AnchorPane.topAnchor="0.0">
<menus>
<Menu fx:id="fileMenu" mnemonicParsing="false" text="File">
<items>
<MenuItem fx:id="openFileMenuItem" mnemonicParsing="false" text="Open..." />
<Menu fx:id="openRecentMenu" mnemonicParsing="false" text="Open recent..." />
<SeparatorMenuItem mnemonicParsing="false" />
<MenuItem fx:id="exportToTestCodeMenuItem" mnemonicParsing="false" text="Export code to test code" />
<MenuItem fx:id="exportXPathMenuItem" mnemonicParsing="false" text="Export XPath to rule" />
<Menu fx:id="openRecentMenu" mnemonicParsing="false" text="Open recent" />
</items>
</Menu>
<Menu mnemonicParsing="false" text="Configuration">
<items>
<MenuItem fx:id="setupAuxclasspathMenuItem" mnemonicParsing="false"
text="Set up the auxclasspath" >
<MenuItem fx:id="setupAuxclasspathMenuItem" mnemonicParsing="false" text="Set up the auxclasspath...">
</MenuItem>
</items>
</Menu>
<Menu mnemonicParsing="false" text="About">
<items>
<MenuItem mnemonicParsing="false" text="About"/>
<MenuItem fx:id="licenseMenuItem" mnemonicParsing="false" text="License"/>
<!-- TODO add link to doc pages -->
<!--<MenuItem mnemonicParsing="false" text="About" />-->
<MenuItem fx:id="licenseMenuItem" mnemonicParsing="false" text="License..." />
</items>
</Menu>
@@ -74,23 +69,6 @@
<bottom>
<ToolBar id="main-toolbar" prefHeight="40.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<items>
<HBox alignment="CENTER" spacing="10.0">
<children>
<Label text="Language Version:" />
<ChoiceBox fx:id="languageChoiceBox" maxWidth="115.0" prefWidth="115.0" HBox.hgrow="NEVER">
<padding>
<Insets bottom="-3.0" left="-3.0" right="-3.0" top="-3.0" />
</padding>
</ChoiceBox>
<Pane prefWidth="10.0" HBox.hgrow="NEVER" />
<Label text="XPath Version:" />
<ChoiceBox maxWidth="115.0" prefWidth="115.0" fx:id="xpathVersionChoiceBox">
<padding>
<Insets bottom="-3.0" left="-3.0" right="-3.0" top="-3.0" />
</padding>
</ChoiceBox>
</children>
</HBox>
<Pane HBox.hgrow="ALWAYS" />
<ToggleButton fx:id="bottomTabsToggle" mnemonicParsing="false" selected="true" styleClass="expand-toggle" />
</items>

View File

@@ -2,42 +2,68 @@
<!-- One editor, ie source + ast view -->
<?import org.kordamp.ikonli.javafx.FontIcon?>
<?import net.sourceforge.pmd.util.fxdesigner.util.codearea.HighlightLayerCodeArea?>
<?import net.sourceforge.pmd.util.fxdesigner.util.controls.ToolbarTitledPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.ToolBar?>
<?import javafx.scene.control.TreeView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<SplitPane dividerPositions="0.5" prefHeight="400.0" prefWidth="500.0" styleClass="accent-header" stylesheets="@../css/designer.css" BorderPane.alignment="CENTER" xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="net.sourceforge.pmd.util.fxdesigner.SourceEditorController">
<SplitPane dividerPositions="0.5, 0.5"
prefHeight="400.0"
prefWidth="500.0"
styleClass="accent-header"
stylesheets="@../css/designer.css"
BorderPane.alignment="CENTER"
xmlns="http://javafx.com/javafx/8.0.172-ea"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="net.sourceforge.pmd.util.fxdesigner.SourceEditorController">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<AnchorPane>
<children>
<BorderPane prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<center>
<HighlightLayerCodeArea fx:id="codeEditorArea" idEnum="net.sourceforge.pmd.util.fxdesigner.SourceEditorController$StyleLayerIds"
prefHeight="346.0" prefWidth="445.0" stylesheets="@../css/editor-theme.css" BorderPane.alignment="CENTER">
<ToolbarTitledPane
collapsible="false"
fx:id="editorTitledPane"
styleClass="accent-header,full-size-title"
title="Source Code"
AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<toolbarItems>
<MenuButton mnemonicParsing="false" styleClass="menu-button-no-arrow,icon-button"
fx:id="languageSelectionMenuButton">
<graphic>
<FontIcon iconLiteral="fas-cog" />
</graphic>
<items>
</items>
</MenuButton>
</toolbarItems>
<content>
<HighlightLayerCodeArea fx:id="codeEditorArea"
idEnum="net.sourceforge.pmd.util.fxdesigner.SourceEditorController$StyleLayerIds"
stylesheets="@../css/editor-theme.css"
BorderPane.alignment="CENTER">
<BorderPane.margin>
<Insets />
</BorderPane.margin>
</HighlightLayerCodeArea>
</center>
<top>
<ToolBar prefHeight="40.0" prefWidth="200.0" styleClass="accent-header" BorderPane.alignment="CENTER">
<items>
<Label text="Source code" />
</items>
</ToolBar>
</top>
</BorderPane>
<!--<TextArea />-->
</content>
</ToolbarTitledPane>
</children>
</AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<BorderPane prefHeight="200.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<center>
<TreeView fx:id="astTreeView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER"/>
<TreeView fx:id="astTreeView" prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER" />
</center>
<top>
<ToolBar prefHeight="40.0" prefWidth="200.0" styleClass="accent-header" BorderPane.alignment="CENTER">

View File

@@ -1,23 +1,51 @@
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.String?>
<?import org.kordamp.ikonli.javafx.FontIcon?>
<?import net.sourceforge.pmd.util.fxdesigner.util.codearea.SyntaxHighlightingCodeArea?>
<?import net.sourceforge.pmd.util.fxdesigner.util.controls.PropertyTableView?>
<?import net.sourceforge.pmd.util.fxdesigner.util.controls.ToolbarTitledPane?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.MenuButton?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.control.Tooltip?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="1200.0" stylesheets="@../css/designer.css" xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1" fx:controller="net.sourceforge.pmd.util.fxdesigner.XPathPanelController">
<?import javafx.scene.shape.SVGPath?>
<AnchorPane minHeight="0.0"
minWidth="0.0"
prefHeight="180.0"
prefWidth="1200.0"
stylesheets="@../css/designer.css"
xmlns="http://javafx.com/javafx/8.0.172-ea"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="net.sourceforge.pmd.util.fxdesigner.XPathPanelController">
<children>
<SplitPane dividerPositions="0.2, 0.8" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<SplitPane dividerPositions="0.2, 0.8"
AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<items>
<AnchorPane>
<children>
<TitledPane animated="false" collapsible="false" styleClass="accent-header" text="Properties" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<TitledPane animated="false"
collapsible="false"
styleClass="accent-header"
text="Properties"
AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<content>
<AnchorPane>
<PropertyTableView fx:id="propertyTableView" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<PropertyTableView fx:id="propertyTableView"
AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0" />
<padding>
<Insets bottom="-0.5" left="-0.5" right="-0.5" top="-0.5" />
</padding>
@@ -28,24 +56,68 @@
</AnchorPane>
<AnchorPane>
<children>
<TitledPane collapsible="false" styleClass="accent-header" text="XPath Expression" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<ToolbarTitledPane fx:id="expressionTitledPane"
styleClass="accent-header"
collapsible="false"
title="XPath Expression"
AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<toolbarItems>
<MenuButton mnemonicParsing="false"
styleClass="menu-button-no-arrow,icon-button"
fx:id="xpathVersionMenuButton">
<graphic>
<FontIcon iconLiteral="fas-cog" />
</graphic>
<tooltip>
<Tooltip>
<text>
<String fx:value="Configure XPath version" />
</text>
</Tooltip>
</tooltip>
<items>
<!-- Populated in the controller -->
</items>
</MenuButton>
<Button fx:id="exportXpathToRuleButton" mnemonicParsing="false" styleClass="icon-button">
<graphic>
<!--Needs FA 5.1.0 -->
<!--<FontIcon iconLiteral="fas-file-export" />-->
<SVGPath styleClass="svg-icon"
scaleX="0.024" scaleY="0.024"
content="M384 121.9c0-6.3-2.5-12.4-7-16.9L279.1 7c-4.5-4.5-10.6-7-17-7H256v128h128v-6.1zM192 336v-32c0-8.84 7.16-16 16-16h176V160H248c-13.2 0-24-10.8-24-24V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V352H208c-8.84 0-16-7.16-16-16zm379.05-28.02l-95.7-96.43c-10.06-10.14-27.36-3.01-27.36 11.27V288H384v64h63.99v65.18c0 14.28 17.29 21.41 27.36 11.27l95.7-96.42c6.6-6.66 6.6-17.4 0-24.05z" />
</graphic>
<tooltip>
<Tooltip>
<text>
<String fx:value="Export XPath to XML rule" />
</text>
</Tooltip>
</tooltip>
</Button>
</toolbarItems>
<content>
<SyntaxHighlightingCodeArea stylesheets="@../css/editor-theme.css"
fx:id="xpathExpressionArea">
</SyntaxHighlightingCodeArea>
<SyntaxHighlightingCodeArea stylesheets="@../css/editor-theme.css" fx:id="xpathExpressionArea" />
</content>
</TitledPane>
</ToolbarTitledPane>
</children>
</AnchorPane>
<AnchorPane>
<children>
<TitledPane fx:id="violationsTitledPane" animated="false"
collapsible="false" styleClass="accent-header"
text="Matched Nodes" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
<TitledPane fx:id="violationsTitledPane"
animated="false"
collapsible="false"
styleClass="accent-header"
text="Matched Nodes"
AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0"
AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0">
<content>
<ListView fx:id="xpathResultListView" stylesheets="@../css/syntax-highlighting.css" />
<ListView stylesheets="@../css/syntax-highlighting.css" fx:id="xpathResultListView" />
</content>
</TitledPane>
</children>

View File

@@ -43,6 +43,9 @@
@selection-focus-color: lighten(royalblue, 20%);
@normal-font-size: 10pt;
@smaller-font-size: 9pt;
// mixin to fix the width of a component
.fix-width(@width) {
-fx-pref-width: @width;
@@ -57,6 +60,11 @@
-fx-max-height: @height;
}
.force-square(@side) {
.fix-width(@side);
.fix-height(@side);
}
// Mixin to iterate over the range [0, @i]
// * The selector in which the mixin is called is appended with a class depth-x,
// where x is the index of the color in the stack

View File

@@ -16,6 +16,9 @@
#d62c49,
#ff6a7a;
.ikonli-font-icon {
-fx-icon-size: 14;
}
.list-view, .tree-view, .table-view {
-fx-selection-bar: @selection-focus-color;
@@ -131,7 +134,7 @@
.titled-pane .title,
.info-title-bar {
-fx-font-size: 9pt;
-fx-font-size: @smaller-font-size;
-fx-pref-height: 24.0;
-fx-border-radius: 0.0;
-fx-background-radius: 0.0;
@@ -140,42 +143,118 @@
&.info-title-bar .label {
-fx-padding: 0 0 0 6;
}
}
// TODO convert every header to ToolbarTitledPane and get rid of info-title-bar
.info-title-bar {
.fix-height(30);
}
.titled-pane.full-size-title {
& > .title {
.fix-height(30);
}
&.tool-bar-title > .title > .tool-bar > .container > .label {
// TODO we can make that bigger when all titled panes rely on ToolbarTitledPane
-fx-font-size: @smaller-font-size;
}
}
// Supports the ToolbarTitledPane
.titled-pane.tool-bar-title > .title {
-fx-padding: 0 6 0 6;
& > .tool-bar {
-fx-background-color: transparent;
& > .container {
-fx-background-insets: 0;
-fx-border-insets: 0;
-fx-border-image-insets: 0;
& > .label {
-fx-background-color: transparent;
-fx-font-size: @smaller-font-size;
}
.separator {
//-fx-background-color: transparent;
-fx-padding: 4 2 4 2;
.line {
-fx-border-style: none solid none none;
-fx-border-width: 0.5px;
-fx-border-color: @fx-text-fill;
}
}
.menu-bar {
-fx-background-color: transparent;
}
}
}
}
.menu-button.menu-button-no-arrow .arrow-button {
&, * {
-fx-padding: 0;
.fix-height(0);
.fix-width(0);
}
}
// This is used for buttons that have just an icon and no text
.icon-button {
-fx-background-color: transparent;
-fx-border-width: 0;
-fx-border-insets: 0;
.force-square(24);
&, * {
-fx-graphic-text-gap: 0;
}
&.menu-button .ikonli-font-icon {
// Fixes a bug where the cog icon is misplaced to the right
// Probably needs to be synced with icon size
-fx-translate-x: -4;
}
.ikonli-font-icon, .svg-icon {
-fx-fill: @fx-text-fill;
}
&:showing, &:hover {
-fx-background-color: @selection-focus-color;
}
.label {
-fx-background-color: transparent;
//-fx-padding: 0;
}
}
#main-horizontal-split-pane > .split-pane-divider {
-fx-background-color: @darker-accent-focus;
}
// TODO use ToolbarTitledPane everywhere and simplify stylesheets
#main-toolbar,
.tool-bar.accent-header,
.split-pane.accent-header > .split-pane-divider {
.fix-height(30);
-fx-background-color: @app-darker-slate-color;
}
.tool-bar {
.fix-height(30);
-fx-border-color: transparent;
-fx-border-width: .6;
.button, .choice-box {
-fx-background-color: @app-base-color;
-fx-border-color: @darker-accent-border;
-fx-border-radius: 3;
}
.button {
-fx-padding: -3 5 -3 5;
}
}
// This is used for buttons that have just an icon and no text
.button.icon-button {
-fx-pref-width: 20;
-fx-pref-height: 20;
}
/* This is the special button to reduce the lower split pane. */
.toggle-button.expand-toggle {
-fx-background-color: -fx-mark-highlight-color, -fx-mark-color;
@@ -192,7 +271,6 @@
}
}
.tab {
-fx-background-insets: 0.0;
-fx-background-radius: 0.0;