From f9b443a1f617f80b5f82b9a754d2f10d602aa7b6 Mon Sep 17 00:00:00 2001 From: Florian Enner Date: Thu, 10 Aug 2023 13:59:26 +0200 Subject: [PATCH] work in progress for stylable datasets that are part of the renderer --- .../main/java/io/fair_acc/chartfx/Chart.java | 13 ++- .../fair_acc/chartfx/renderer/Renderer.java | 3 + .../renderer/spi/AbstractRenderer.java | 28 +++++ .../chartfx/renderer/spi/GridRenderer.java | 14 +-- .../renderer/spi/MetaDataRenderer.java | 6 ++ .../fair_acc/chartfx/ui/css/DataSetNode.java | 101 ++++++++++++++++++ 6 files changed, 158 insertions(+), 7 deletions(-) create mode 100644 chartfx-chart/src/main/java/io/fair_acc/chartfx/ui/css/DataSetNode.java diff --git a/chartfx-chart/src/main/java/io/fair_acc/chartfx/Chart.java b/chartfx-chart/src/main/java/io/fair_acc/chartfx/Chart.java index 76d0ee996..f17b3a947 100644 --- a/chartfx-chart/src/main/java/io/fair_acc/chartfx/Chart.java +++ b/chartfx-chart/src/main/java/io/fair_acc/chartfx/Chart.java @@ -6,6 +6,7 @@ import java.util.stream.Collectors; import io.fair_acc.chartfx.renderer.spi.AbstractRenderer; +import io.fair_acc.chartfx.ui.css.DataSetNode; import io.fair_acc.chartfx.ui.css.StyleGroup; import io.fair_acc.chartfx.ui.css.StyleUtil; import io.fair_acc.chartfx.ui.layout.TitleLabel; @@ -791,7 +792,18 @@ protected void datasetsChanged(final ListChangeListener.Change change) { FXUtils.assertJavaFxThread(); while (change.next()) { - // TODO: how to work with renderer axes that are not in the SceneGraph? The length would never get set. // handle added renderer for (Renderer renderer : change.getAddedSubList()) { diff --git a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/Renderer.java b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/Renderer.java index 94713cd1c..73ff50c72 100644 --- a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/Renderer.java +++ b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/Renderer.java @@ -2,6 +2,7 @@ import java.util.List; +import io.fair_acc.chartfx.ui.css.DataSetNode; import javafx.beans.property.BooleanProperty; import javafx.collections.ObservableList; import javafx.scene.canvas.Canvas; @@ -36,6 +37,8 @@ public interface Renderer { ObservableList getDatasetsCopy(); + ObservableList getDatasetNodes(); + /** * Optional method that allows the renderer make layout changes after axes and dataset limits are known */ diff --git a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/AbstractRenderer.java b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/AbstractRenderer.java index 35c7edadb..3028864aa 100644 --- a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/AbstractRenderer.java +++ b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/AbstractRenderer.java @@ -2,11 +2,13 @@ import io.fair_acc.chartfx.Chart; import io.fair_acc.chartfx.ui.css.CssPropertyFactory; +import io.fair_acc.chartfx.ui.css.DataSetNode; import io.fair_acc.chartfx.ui.css.StyleUtil; import io.fair_acc.chartfx.utils.PropUtil; import io.fair_acc.dataset.events.ChartBits; import javafx.beans.property.*; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.css.CssMetaData; import javafx.css.Styleable; @@ -26,6 +28,7 @@ import java.util.List; import java.util.function.IntSupplier; +import java.util.stream.Collectors; /** * @author rstein @@ -35,12 +38,33 @@ public abstract class AbstractRenderer extends Parent implem protected final StyleableBooleanProperty showInLegend = css().createBooleanProperty(this, "showInLegend", true); private final ObservableList datasets = FXCollections.observableArrayList(); + private final ObservableList dataSetNodes = FXCollections.observableArrayList(); private final ObservableList axesList = FXCollections.observableList(new NoDuplicatesList<>()); private final ObjectProperty chart = new SimpleObjectProperty<>(); + protected DataSetNode createNode(DataSet dataSet) { + // Reuse existing nodes when possible + for (DataSetNode dataSetNode : dataSetNodes) { + if (dataSetNode.getDataSet() == dataSet) { + return dataSetNode; + } + } + return new DataSetNode(dataSet); + } + public AbstractRenderer() { StyleUtil.addStyles(this, "renderer"); PropUtil.runOnChange(() -> fireInvalidated(ChartBits.ChartLegend), showInLegend); + dataSetNodes.addListener((ListChangeListener) c -> { + getChildren().setAll(dataSetNodes); + }); + datasets.addListener((ListChangeListener) c -> { + dataSetNodes.setAll(datasets.stream().distinct().map(this::createNode).collect(Collectors.toList())); + int i = 0; + for (DataSetNode dataSetNode : dataSetNodes) { + dataSetNode.setLocalIndex(i++); + } + }); } @Override @@ -53,6 +77,10 @@ public ObservableList getDatasets() { return datasets; } + public ObservableList getDatasetNodes() { + return dataSetNodes; + } + @Override public ObservableList getDatasetsCopy() { return getDatasetsCopy(getDatasets()); diff --git a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/GridRenderer.java b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/GridRenderer.java index 41505edc2..6aafa38b8 100644 --- a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/GridRenderer.java +++ b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/GridRenderer.java @@ -4,10 +4,7 @@ import java.util.Collections; import java.util.List; -import io.fair_acc.chartfx.ui.css.CssPropertyFactory; -import io.fair_acc.chartfx.ui.css.LineStyle; -import io.fair_acc.chartfx.ui.css.StyleGroup; -import io.fair_acc.chartfx.ui.css.StyleUtil; +import io.fair_acc.chartfx.ui.css.*; import javafx.beans.property.BooleanProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -267,12 +264,17 @@ public ObservableList getAxes() { @Override public ObservableList getDatasets() { - return null; + return FXCollections.emptyObservableList(); } @Override public ObservableList getDatasetsCopy() { - return null; + return FXCollections.emptyObservableList(); + } + + @Override + public ObservableList getDatasetNodes() { + return FXCollections.emptyObservableList(); } /** diff --git a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/MetaDataRenderer.java b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/MetaDataRenderer.java index 194c3d5a4..6bfb059c3 100644 --- a/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/MetaDataRenderer.java +++ b/chartfx-chart/src/main/java/io/fair_acc/chartfx/renderer/spi/MetaDataRenderer.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.List; +import io.fair_acc.chartfx.ui.css.DataSetNode; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; @@ -165,6 +166,11 @@ public ObservableList getDatasetsCopy() { return FXCollections.observableArrayList(); } + @Override + public ObservableList getDatasetNodes() { + return FXCollections.emptyObservableList(); + } + protected List getDataSetsWithMetaData(List dataSets) { final List list = new ArrayList<>(); for (final DataSet dataSet : dataSets) { diff --git a/chartfx-chart/src/main/java/io/fair_acc/chartfx/ui/css/DataSetNode.java b/chartfx-chart/src/main/java/io/fair_acc/chartfx/ui/css/DataSetNode.java new file mode 100644 index 000000000..827cdc23f --- /dev/null +++ b/chartfx-chart/src/main/java/io/fair_acc/chartfx/ui/css/DataSetNode.java @@ -0,0 +1,101 @@ +package io.fair_acc.chartfx.ui.css; + +import io.fair_acc.chartfx.XYChartCss; +import io.fair_acc.chartfx.utils.PropUtil; +import io.fair_acc.dataset.DataSet; +import io.fair_acc.dataset.event.EventSource; +import io.fair_acc.dataset.events.BitState; +import io.fair_acc.dataset.utils.AssertUtils; +import javafx.beans.property.IntegerProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.css.*; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.paint.Paint; +import javafx.scene.shape.Shape; + +import java.util.List; + +/** + * A dataset wrapper that lives in the SceneGraph for CSS styling + * + * @author ennerf + */ +public class DataSetNode extends TextStyle implements EventSource { + + public DataSetNode(DataSet dataSet) { + StyleUtil.styleNode(this, "dataset"); + this.dataSet = AssertUtils.notNull("dataSet", dataSet); + setVisible(dataSet.isVisible()); + if (!PropUtil.isNullOrEmpty(dataSet.getStyle())) { + setStyle(dataSet.getStyle()); + } + } + + // Index within the renderer set + final IntegerProperty localIndex = new SimpleIntegerProperty(); + + // Index within all chart sets + final IntegerProperty globalIndex = new SimpleIntegerProperty(); + + // Offset for the color indexing + final StyleableIntegerProperty dsLayoutOffset = CSS.createIntegerProperty(this, XYChartCss.DATASET_LAYOUT_OFFSET, 0); + + // A stylable local index. TODO: should this really be settable? + final StyleableIntegerProperty dsIndex = CSS.createIntegerProperty(this, XYChartCss.DATASET_INDEX, 0); + + final StyleableDoubleProperty intensity = CSS.createDoubleProperty(this, XYChartCss.DATASET_INTENSITY, 100); + final StyleableBooleanProperty showInLegend = CSS.createBooleanProperty(this, XYChartCss.DATASET_SHOW_IN_LEGEND, true); + + @Override + public Node getStyleableNode() { + return this; + } + + @Override + public BitState getBitState() { + return dataSet.getBitState(); + } + + public DataSet getDataSet() { + return dataSet; + } + + public int getLocalIndex() { + return localIndex.get(); + } + + public IntegerProperty localIndexProperty() { + return localIndex; + } + + public void setLocalIndex(int localIndex) { + this.localIndex.set(localIndex); + } + + public int getGlobalIndex() { + return globalIndex.get(); + } + + public IntegerProperty globalIndexProperty() { + return globalIndex; + } + + public void setGlobalIndex(int globalIndex) { + this.globalIndex.set(globalIndex); + } + + public static List> getClassCssMetaData() { + return CSS.getCssMetaData(); + } + + @Override + public List> getCssMetaData() { + return getClassCssMetaData(); + } + + private final DataSet dataSet; + + private static final CssPropertyFactory CSS = new CssPropertyFactory<>(Shape.getClassCssMetaData()); + +}