From 7ce893369790e535c01825b7b48b30b64fdc48aa Mon Sep 17 00:00:00 2001
From: tobias <thinkdifferent055@gmail.com>
Date: Thu, 26 Nov 2020 20:25:44 +0100
Subject: [PATCH] Use listener based style classes for desktop pad view

---
 .../layout/desktop/pad/DesktopPadView.java    | 67 +++++++------------
 .../de/tobias/playpad/view/pad/PadButton.java | 13 +---
 .../de/tobias/playpad/view/pad/PadHBox.java   | 28 ++++++++
 .../de/tobias/playpad/view/pad/PadLabel.java  | 39 ++++++-----
 .../playpad/view/pad/PadProgressBar.java      | 25 +++++++
 .../tobias/playpad/view/pad/PadStackPane.java | 24 +++++++
 .../playpad/view/pad/PadStyleClasses.java     | 12 ++++
 .../de/tobias/playpad/view/pad/PadVBox.java   | 28 ++++++++
 .../playpad/view/pad/StyleIndexListener.java  | 32 +++++++++
 9 files changed, 196 insertions(+), 72 deletions(-)
 create mode 100644 PlayWall/src/main/java/de/tobias/playpad/view/pad/PadHBox.java
 create mode 100644 PlayWall/src/main/java/de/tobias/playpad/view/pad/PadProgressBar.java
 create mode 100644 PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStackPane.java
 create mode 100644 PlayWall/src/main/java/de/tobias/playpad/view/pad/PadVBox.java
 create mode 100644 PlayWall/src/main/java/de/tobias/playpad/view/pad/StyleIndexListener.java

diff --git a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java
index cf386db0..4a4468d3 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/layout/desktop/pad/DesktopPadView.java
@@ -24,9 +24,7 @@ import de.tobias.playpad.registry.NoSuchComponentException;
 import de.tobias.playpad.util.NodeWalker;
 import de.tobias.playpad.view.EmptyPadView;
 import de.tobias.playpad.view.PseudoClasses;
-import de.tobias.playpad.view.pad.PadButton;
-import de.tobias.playpad.view.pad.PadIndexable;
-import de.tobias.playpad.view.pad.PadLabel;
+import de.tobias.playpad.view.pad.*;
 import javafx.beans.property.Property;
 import javafx.css.PseudoClass;
 import javafx.geometry.Pos;
@@ -37,18 +35,17 @@ import javafx.scene.control.ProgressBar;
 import javafx.scene.layout.*;
 import javafx.scene.paint.Color;
 
-import static de.tobias.playpad.view.pad.PadStyleClasses.STYLE_CLASS_PAD_INFO;
-import static de.tobias.playpad.view.pad.PadStyleClasses.STYLE_CLASS_PAD_INFO_INDEX;
+import static de.tobias.playpad.view.pad.PadStyleClasses.*;
 
 public class DesktopPadView implements IPadView {
 
-	private PadLabel indexLabel;
-	private PadLabel loopLabel;
-	private PadLabel triggerLabel;
-	private PadLabel errorLabel;
+	private Label indexLabel;
+	private Label loopLabel;
+	private Label triggerLabel;
+	private Label errorLabel;
 
 	private HBox infoBox;
-	private PadLabel timeLabel;
+	private Label timeLabel;
 
 	private HBox preview;
 	private IPadContentView previewContent;
@@ -56,21 +53,21 @@ public class DesktopPadView implements IPadView {
 	private FontIcon notFoundLabel;
 
 	private ProgressBar playBar;
-	private PadButton playButton;
-	private PadButton pauseButton;
-	private PadButton stopButton;
-	private PadButton newButton;
-	private PadButton settingsButton;
+	private Button playButton;
+	private Button pauseButton;
+	private Button stopButton;
+	private Button newButton;
+	private Button settingsButton;
 	private HBox buttonBox;
 
 	private StackPane superRoot;
 	private VBox root;
 	private BusyView busyView;
 
-	private VBox cueInContainer;
 	private Label cueInLayer;
 
-	private transient DesktopPadViewController controller; // Reference to its controller
+	// Reference to its controller
+	private final transient DesktopPadViewController controller;
 
 	public DesktopPadView(DesktopMainLayoutFactory connect) {
 		controller = new DesktopPadViewController(this, connect);
@@ -78,23 +75,22 @@ public class DesktopPadView implements IPadView {
 	}
 
 	private void setupView() {
-		superRoot = new StackPane();
-		root = new VBox();
+		superRoot = new PadStackPane(STYLE_CLASS_PAD, STYLE_CLASS_PAD_INDEX);
+		root = new PadVBox(STYLE_CLASS_PAD_BUTTON_ROOT);
 		busyView = new BusyView(superRoot);
 
-		cueInLayer = new Label();
+		cueInLayer = PadLabel.empty(STYLE_CLASS_PAD_CUE_IN, STYLE_CLASS_PAD_CUE_IN_INDEX);
 		cueInLayer.prefHeightProperty().bind(root.heightProperty());
-		cueInContainer = new VBox(cueInLayer);
+		VBox cueInContainer = new VBox(cueInLayer);
 
-		indexLabel = new PadLabel("", STYLE_CLASS_PAD_INFO, STYLE_CLASS_PAD_INFO_INDEX);
-		timeLabel = new PadLabel("", STYLE_CLASS_PAD_INFO, STYLE_CLASS_PAD_INFO_INDEX);
+		indexLabel = PadLabel.empty(STYLE_CLASS_PAD_INFO, STYLE_CLASS_PAD_INFO_INDEX);
+		timeLabel = PadLabel.empty(STYLE_CLASS_PAD_INFO, STYLE_CLASS_PAD_INFO_INDEX);
 
 		loopLabel = new PadLabel(new FontIcon(FontAwesomeType.REPEAT));
 		triggerLabel = new PadLabel(new FontIcon(FontAwesomeType.EXTERNAL_LINK));
 		errorLabel = new PadLabel(new FontIcon(FontAwesomeType.WARNING));
 
-		infoBox = new HBox(); // childern in addDefaultButton()
-		infoBox.setSpacing(5);
+		infoBox = new PadHBox(5);
 
 		preview = new HBox();
 		HBox.setHgrow(preview, Priority.ALWAYS);
@@ -104,7 +100,7 @@ public class DesktopPadView implements IPadView {
 		timeLabel.setMaxWidth(Double.MAX_VALUE);
 		timeLabel.setAlignment(Pos.CENTER_RIGHT);
 
-		playBar = new ProgressBar(0);
+		playBar = new PadProgressBar(0, STYLE_CLASS_PAD_PLAYBAR, STYLE_CLASS_PAD_PLAYBAR_INDEX);
 		playBar.prefWidthProperty().bind(root.widthProperty());
 
 		// Buttons
@@ -120,11 +116,10 @@ public class DesktopPadView implements IPadView {
 		notFoundLabel.setOpacity(0.75);
 		notFoundLabel.setSize(80);
 		notFoundLabel.setMouseTransparent(true);
-
 		notFoundLabel.setVisible(false);
 
 		// Button HBOX
-		buttonBox = new HBox(); // childern in addDefaultButton()
+		buttonBox = new PadHBox(STYLE_CLASS_PAD_BUTTON_BOX);
 
 		root.getChildren().addAll(infoBox, preview, playBar, buttonBox);
 		superRoot.getChildren().addAll(cueInContainer, root, notFoundLabel);
@@ -300,16 +295,8 @@ public class DesktopPadView implements IPadView {
 
 	@Override
 	public void applyStyleClasses(PadIndex index) {
-		superRoot.getStyleClass().addAll("pad", "pad" + index);
-		cueInLayer.getStyleClass().addAll("pad-cue-in", "pad" + index + "-cue-in");
-
 		preview.getChildren().forEach(i -> i.getStyleClass().addAll("pad-title", "pad" + index + "-title"));
 
-		playBar.getStyleClass().addAll("pad-playbar", "pad" + index + "-playbar");
-
-		buttonBox.getStyleClass().add("pad-button-box");
-		root.getStyleClass().add("pad-root");
-
 		NodeWalker.getAllNodes(getRootNode())
 				.stream()
 				.filter(node -> node instanceof PadIndexable)
@@ -318,16 +305,8 @@ public class DesktopPadView implements IPadView {
 
 	@Override
 	public void removeStyleClasses() {
-		superRoot.getStyleClass().removeIf(c -> c.startsWith("pad"));
-		cueInLayer.getStyleClass().removeIf(c -> c.startsWith("pad"));
-
 		preview.getChildren().forEach(i -> i.getStyleClass().removeIf(c -> c.startsWith("pad")));
 
-		playBar.getStyleClass().removeIf(c -> c.startsWith("pad"));
-
-		buttonBox.getStyleClass().remove("pad-button-box");
-		root.getStyleClass().remove("pad-root");
-
 		NodeWalker.getAllNodes(getRootNode())
 				.stream()
 				.filter(node -> node instanceof PadIndexable)
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadButton.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadButton.java
index fcfdf84b..9cfcd183 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadButton.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadButton.java
@@ -21,17 +21,8 @@ public class PadButton extends Button implements PadIndexable {
 		setOnAction(value);
 
 		indexProperty = new SimpleObjectProperty<>();
-		indexProperty.addListener((observable, oldValue, newValue) -> {
-			if (oldValue != null) {
-				getStyleClass().removeAll(STYLE_CLASS_PAD_BUTTON, replaceIndex(STYLE_CLASS_PAD_BUTTON_INDEX, oldValue));
-				getGraphic().getStyleClass().removeAll(STYLE_CLASS_PAD_ICON, replaceIndex(STYLE_CLASS_PAD_ICON_INDEX, oldValue));
-			}
-
-			if (newValue != null) {
-				getStyleClass().addAll(STYLE_CLASS_PAD_BUTTON, replaceIndex(STYLE_CLASS_PAD_BUTTON_INDEX, newValue));
-				getGraphic().getStyleClass().addAll(STYLE_CLASS_PAD_ICON, replaceIndex(STYLE_CLASS_PAD_ICON_INDEX, newValue));
-			}
-		});
+		indexProperty.addListener(new StyleIndexListener(this, STYLE_CLASS_PAD_BUTTON, STYLE_CLASS_PAD_BUTTON_INDEX));
+		indexProperty.addListener(new StyleIndexListener(getGraphic(), STYLE_CLASS_PAD_ICON, STYLE_CLASS_PAD_ICON_INDEX));
 	}
 
 	public PadIndex getIndex() {
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadHBox.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadHBox.java
new file mode 100644
index 00000000..d82de13a
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadHBox.java
@@ -0,0 +1,28 @@
+package de.tobias.playpad.view.pad;
+
+import de.tobias.playpad.project.page.PadIndex;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.layout.HBox;
+
+public class PadHBox extends HBox implements PadIndexable {
+
+	private final ObjectProperty<PadIndex> indexProperty;
+
+	public PadHBox(String... styleClasses) {
+		this(0, styleClasses);
+	}
+	public PadHBox(double spacing, String... styleClasses) {
+		super(spacing);
+		indexProperty = new SimpleObjectProperty<>();
+		indexProperty.addListener(new StyleIndexListener(this, styleClasses));
+	}
+
+	public PadIndex getIndex() {
+		return indexProperty.get();
+	}
+
+	public void setIndex(PadIndex index) {
+		indexProperty.set(index);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadLabel.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadLabel.java
index abbee135..7f8c9007 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadLabel.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadLabel.java
@@ -6,11 +6,17 @@ import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.scene.control.Label;
 
-import static de.tobias.playpad.view.pad.PadStyleClasses.*;
+import static de.tobias.playpad.view.pad.PadStyleClasses.STYLE_CLASS_PAD_ICON;
+import static de.tobias.playpad.view.pad.PadStyleClasses.STYLE_CLASS_PAD_ICON_INDEX;
 
 public class PadLabel extends Label implements PadIndexable {
 
 	private final ObjectProperty<PadIndex> indexProperty;
+	private StyleIndexListener graphicsListener;
+
+	public static PadLabel empty(String... styleClasses) {
+		return new PadLabel("", styleClasses);
+	}
 
 	public PadLabel(FontIcon icon, String... styleClasses) {
 		this("", styleClasses);
@@ -21,27 +27,26 @@ public class PadLabel extends Label implements PadIndexable {
 		super(text);
 
 		indexProperty = new SimpleObjectProperty<>();
-		indexProperty.addListener((observable, oldValue, newValue) -> {
-			if (oldValue != null) {
-				for (String styleClass : styleClasses) {
-					getStyleClass().remove(PadStyleClasses.replaceIndex(styleClass, oldValue));
-				}
-				if (getGraphic() != null) {
-					getGraphic().getStyleClass().removeAll(STYLE_CLASS_PAD_ICON, replaceIndex(STYLE_CLASS_PAD_ICON_INDEX, oldValue));
-				}
-			}
+		indexProperty.addListener(new StyleIndexListener(this, styleClasses));
+		initStyleGraphicsListener();
 
-			if (newValue != null) {
-				for (String styleClass : styleClasses) {
-					getStyleClass().add(PadStyleClasses.replaceIndex(styleClass, newValue));
-				}
-				if (getGraphic() != null) {
-					getGraphic().getStyleClass().addAll(STYLE_CLASS_PAD_ICON, replaceIndex(STYLE_CLASS_PAD_ICON_INDEX, newValue));
-				}
+		graphicProperty().addListener(observable -> {
+			if (graphicsListener != null) {
+				indexProperty.removeListener(graphicsListener);
+				graphicsListener = null;
+
+				initStyleGraphicsListener();
 			}
 		});
 	}
 
+	private void initStyleGraphicsListener() {
+		if (getGraphic() != null) {
+			graphicsListener = new StyleIndexListener(getGraphic(), STYLE_CLASS_PAD_ICON, STYLE_CLASS_PAD_ICON_INDEX);
+			indexProperty.addListener(graphicsListener);
+		}
+	}
+
 	public PadIndex getIndex() {
 		return indexProperty.get();
 	}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadProgressBar.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadProgressBar.java
new file mode 100644
index 00000000..1d366b8c
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadProgressBar.java
@@ -0,0 +1,25 @@
+package de.tobias.playpad.view.pad;
+
+import de.tobias.playpad.project.page.PadIndex;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.control.ProgressBar;
+
+public class PadProgressBar extends ProgressBar implements PadIndexable {
+
+	private final ObjectProperty<PadIndex> indexProperty;
+
+	public PadProgressBar(double progress, String... styleClasses) {
+		super(progress);
+		indexProperty = new SimpleObjectProperty<>();
+		indexProperty.addListener(new StyleIndexListener(this, styleClasses));
+	}
+
+	public PadIndex getIndex() {
+		return indexProperty.get();
+	}
+
+	public void setIndex(PadIndex index) {
+		indexProperty.set(index);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStackPane.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStackPane.java
new file mode 100644
index 00000000..94d56707
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStackPane.java
@@ -0,0 +1,24 @@
+package de.tobias.playpad.view.pad;
+
+import de.tobias.playpad.project.page.PadIndex;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.layout.StackPane;
+
+public class PadStackPane extends StackPane implements PadIndexable {
+
+	private final ObjectProperty<PadIndex> indexProperty;
+
+	public PadStackPane(String... styleClasses) {
+		indexProperty = new SimpleObjectProperty<>();
+		indexProperty.addListener(new StyleIndexListener(this, styleClasses));
+	}
+
+	public PadIndex getIndex() {
+		return indexProperty.get();
+	}
+
+	public void setIndex(PadIndex index) {
+		indexProperty.set(index);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStyleClasses.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStyleClasses.java
index 65a22399..b610e439 100644
--- a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStyleClasses.java
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadStyleClasses.java
@@ -7,6 +7,9 @@ public class PadStyleClasses {
 	private PadStyleClasses() {
 	}
 
+	public static final String STYLE_CLASS_PAD = "pad";
+	public static final String STYLE_CLASS_PAD_INDEX = "pad${index}";
+
 	public static final String STYLE_CLASS_PAD_BUTTON = "pad-button";
 	public static final String STYLE_CLASS_PAD_BUTTON_INDEX = "pad${index}-button";
 
@@ -19,6 +22,15 @@ public class PadStyleClasses {
 	public static final String STYLE_CLASS_PAD_TITLE = "pad-title";
 	public static final String STYLE_CLASS_PAD_TITLE_INDEX = "pad${index}-title";
 
+	public static final String STYLE_CLASS_PAD_BUTTON_BOX = "pad-button-box";
+	public static final String STYLE_CLASS_PAD_BUTTON_ROOT = "pad-root";
+
+	public static final String STYLE_CLASS_PAD_PLAYBAR = "pad-playbar";
+	public static final String STYLE_CLASS_PAD_PLAYBAR_INDEX = "pad${index}-playbar";
+
+	public static final String STYLE_CLASS_PAD_CUE_IN = "pad-cue-in";
+	public static final String STYLE_CLASS_PAD_CUE_IN_INDEX = "pad${index}-cue-in";
+
 	public static String replaceIndex(String styleClass, PadIndex index) {
 		return styleClass.replace("${index}", String.valueOf(index));
 	}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadVBox.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadVBox.java
new file mode 100644
index 00000000..dc4e140b
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/PadVBox.java
@@ -0,0 +1,28 @@
+package de.tobias.playpad.view.pad;
+
+import de.tobias.playpad.project.page.PadIndex;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.scene.layout.VBox;
+
+public class PadVBox extends VBox implements PadIndexable {
+
+	private final ObjectProperty<PadIndex> indexProperty;
+
+	public PadVBox(String... styleClasses) {
+		this(0, styleClasses);
+	}
+	public PadVBox(double spacing, String... styleClasses) {
+		super(spacing);
+		indexProperty = new SimpleObjectProperty<>();
+		indexProperty.addListener(new StyleIndexListener(this, styleClasses));
+	}
+
+	public PadIndex getIndex() {
+		return indexProperty.get();
+	}
+
+	public void setIndex(PadIndex index) {
+		indexProperty.set(index);
+	}
+}
diff --git a/PlayWall/src/main/java/de/tobias/playpad/view/pad/StyleIndexListener.java b/PlayWall/src/main/java/de/tobias/playpad/view/pad/StyleIndexListener.java
new file mode 100644
index 00000000..de3acf0c
--- /dev/null
+++ b/PlayWall/src/main/java/de/tobias/playpad/view/pad/StyleIndexListener.java
@@ -0,0 +1,32 @@
+package de.tobias.playpad.view.pad;
+
+import de.tobias.playpad.project.page.PadIndex;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.scene.Node;
+
+public class StyleIndexListener implements ChangeListener<PadIndex> {
+
+	private final Node node;
+	private final String[] styleClasses;
+
+	public StyleIndexListener(Node node, String... styleClasses) {
+		this.node = node;
+		this.styleClasses = styleClasses;
+	}
+
+	@Override
+	public void changed(ObservableValue<? extends PadIndex> observable, PadIndex oldValue, PadIndex newValue) {
+		if (oldValue != null) {
+			for (String styleClass : styleClasses) {
+				node.getStyleClass().remove(PadStyleClasses.replaceIndex(styleClass, oldValue));
+			}
+		}
+
+		if (newValue != null) {
+			for (String styleClass : styleClasses) {
+				node.getStyleClass().add(PadStyleClasses.replaceIndex(styleClass, newValue));
+			}
+		}
+	}
+}
-- 
GitLab