implements PR-10b bank composition base components

This commit is contained in:
bQUARKz 2026-03-19 00:43:39 +00:00
parent 49d83e3ff8
commit bd753a8f1e
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
4 changed files with 257 additions and 0 deletions

View File

@ -0,0 +1,74 @@
package p.studio.controls.banks;
import javafx.geometry.Pos;
import javafx.scene.control.Label;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
public final class StudioAssetCapacityMeter extends VBox {
private static final String BASE_SEVERITY_CLASS = "studio-asset-capacity-meter-fill-";
private final AnchorPane track = new AnchorPane();
private final Region fill = new Region();
private final Label label = new Label();
private final Label hint = new Label();
private double progress;
private StudioAssetCapacitySeverity severity = StudioAssetCapacitySeverity.GREEN;
public StudioAssetCapacityMeter() {
setAlignment(Pos.TOP_CENTER);
setSpacing(8);
getStyleClass().add("studio-asset-capacity-meter");
track.getStyleClass().add("studio-asset-capacity-meter-track");
track.setMinWidth(28);
track.setPrefWidth(28);
track.setPrefHeight(180);
VBox.setVgrow(track, Priority.ALWAYS);
fill.getStyleClass().addAll("studio-asset-capacity-meter-fill", BASE_SEVERITY_CLASS + severity.name().toLowerCase());
fill.setManaged(false);
AnchorPane.setLeftAnchor(fill, 0.0d);
AnchorPane.setRightAnchor(fill, 0.0d);
AnchorPane.setBottomAnchor(fill, 0.0d);
track.getChildren().add(fill);
label.getStyleClass().add("studio-asset-capacity-meter-label");
hint.getStyleClass().add("studio-asset-capacity-meter-hint");
hint.setWrapText(true);
track.heightProperty().addListener((ignored, oldValue, newValue) -> refreshFillHeight(newValue.doubleValue()));
getChildren().setAll(track, label, hint);
refreshFillHeight(track.getPrefHeight());
}
public void setProgress(double progress) {
this.progress = Math.max(0.0d, Math.min(1.0d, progress));
refreshFillHeight(track.getHeight() <= 0.0d ? track.getPrefHeight() : track.getHeight());
}
public void setSeverity(StudioAssetCapacitySeverity severity) {
this.severity = severity == null ? StudioAssetCapacitySeverity.GREEN : severity;
fill.getStyleClass().removeIf(styleClass -> styleClass.startsWith(BASE_SEVERITY_CLASS));
fill.getStyleClass().add(BASE_SEVERITY_CLASS + this.severity.name().toLowerCase());
}
public void setLabelText(String text) {
label.setText(text == null ? "" : text);
}
public void setHintText(String text) {
final String safeText = text == null ? "" : text;
hint.setText(safeText);
hint.setVisible(!safeText.isBlank());
hint.setManaged(!safeText.isBlank());
}
private void refreshFillHeight(double trackHeight) {
fill.setPrefHeight(Math.max(0.0d, trackHeight) * progress);
}
}

View File

@ -0,0 +1,7 @@
package p.studio.controls.banks;
public enum StudioAssetCapacitySeverity {
GREEN,
ORANGE,
RED
}

View File

@ -0,0 +1,162 @@
package p.studio.controls.banks;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.IntConsumer;
public abstract class StudioDualListView<T> extends HBox {
private final ObservableList<T> leftItems = FXCollections.observableArrayList();
private final ObservableList<T> rightItems = FXCollections.observableArrayList();
private final ListView<T> leftListView = new ListView<>(leftItems);
private final ListView<T> rightListView = new ListView<>(rightItems);
private final Button moveToRightButton = new Button(">");
private final Button moveToLeftButton = new Button("<");
private final Button moveUpButton = new Button("Up");
private final Button moveDownButton = new Button("Down");
private Consumer<List<T>> onMoveToRight = ignored -> { };
private Consumer<List<T>> onMoveToLeft = ignored -> { };
private IntConsumer onMoveUp = ignored -> { };
private IntConsumer onMoveDown = ignored -> { };
protected StudioDualListView() {
setSpacing(12);
getStyleClass().add("studio-dual-list-view");
leftListView.getStyleClass().add("studio-dual-list-left");
rightListView.getStyleClass().add("studio-dual-list-right");
leftListView.setCellFactory(ignored -> createCell(false));
rightListView.setCellFactory(ignored -> createCell(true));
final VBox leftColumn = createColumn("Available", leftListView);
final VBox rightColumn = createColumn("Selected", rightListView);
final VBox centerActions = new VBox(8, moveToRightButton, moveToLeftButton, moveUpButton, moveDownButton);
centerActions.setAlignment(Pos.CENTER);
centerActions.getStyleClass().add("studio-dual-list-actions");
HBox.setHgrow(leftColumn, Priority.ALWAYS);
HBox.setHgrow(rightColumn, Priority.ALWAYS);
getChildren().setAll(leftColumn, centerActions, rightColumn);
moveToRightButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
moveToLeftButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
moveUpButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
moveDownButton.getStyleClass().addAll("studio-button", "studio-button-secondary");
moveToRightButton.setOnAction(ignored -> onMoveToRight.accept(List.copyOf(leftListView.getSelectionModel().getSelectedItems())));
moveToLeftButton.setOnAction(ignored -> onMoveToLeft.accept(List.copyOf(rightListView.getSelectionModel().getSelectedItems())));
moveUpButton.setOnAction(ignored -> {
final int selectedIndex = rightListView.getSelectionModel().getSelectedIndex();
if (selectedIndex >= 0) {
onMoveUp.accept(selectedIndex);
}
});
moveDownButton.setOnAction(ignored -> {
final int selectedIndex = rightListView.getSelectionModel().getSelectedIndex();
if (selectedIndex >= 0) {
onMoveDown.accept(selectedIndex);
}
});
updateActionState();
leftListView.getSelectionModel().selectedItemProperty().addListener((ignored, oldValue, newValue) -> updateActionState());
rightListView.getSelectionModel().selectedItemProperty().addListener((ignored, oldValue, newValue) -> updateActionState());
}
public void setLeftItems(List<T> items) {
leftItems.setAll(Objects.requireNonNull(items, "items"));
leftListView.refresh();
updateActionState();
}
public void setRightItems(List<T> items) {
rightItems.setAll(Objects.requireNonNull(items, "items"));
rightListView.refresh();
updateActionState();
}
public void setInteractionEnabled(boolean enabled) {
leftListView.setDisable(!enabled);
rightListView.setDisable(!enabled);
moveToRightButton.setDisable(!enabled || leftListView.getSelectionModel().isEmpty());
moveToLeftButton.setDisable(!enabled || rightListView.getSelectionModel().isEmpty());
moveUpButton.setDisable(!enabled || rightListView.getSelectionModel().getSelectedIndex() <= 0);
moveDownButton.setDisable(!enabled || rightListView.getSelectionModel().getSelectedIndex() < 0
|| rightListView.getSelectionModel().getSelectedIndex() >= rightItems.size() - 1);
}
public void setOnMoveToRight(Consumer<List<T>> onMoveToRight) {
this.onMoveToRight = Objects.requireNonNull(onMoveToRight, "onMoveToRight");
}
public void setOnMoveToLeft(Consumer<List<T>> onMoveToLeft) {
this.onMoveToLeft = Objects.requireNonNull(onMoveToLeft, "onMoveToLeft");
}
public void setOnMoveUp(IntConsumer onMoveUp) {
this.onMoveUp = Objects.requireNonNull(onMoveUp, "onMoveUp");
}
public void setOnMoveDown(IntConsumer onMoveDown) {
this.onMoveDown = Objects.requireNonNull(onMoveDown, "onMoveDown");
}
protected abstract String itemText(T item);
protected Node itemGraphic(T item) {
return null;
}
private VBox createColumn(String title, ListView<T> listView) {
final Label titleLabel = new Label(title);
titleLabel.getStyleClass().add("studio-dual-list-title");
final VBox column = new VBox(8, titleLabel, listView);
column.getStyleClass().add("studio-dual-list-column");
VBox.setVgrow(listView, Priority.ALWAYS);
return column;
}
private ListCell<T> createCell(boolean indexed) {
return new ListCell<>() {
@Override
protected void updateItem(T item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText(null);
setGraphic(null);
return;
}
final String text = indexed
? (getIndex() + 1) + ". " + itemText(item)
: itemText(item);
setText(text);
setGraphic(itemGraphic(item));
}
};
}
private void updateActionState() {
final boolean leftSelected = !leftListView.getSelectionModel().isEmpty();
final int rightSelectedIndex = rightListView.getSelectionModel().getSelectedIndex();
moveToRightButton.setDisable(!leftSelected || leftListView.isDisabled());
moveToLeftButton.setDisable(rightSelectedIndex < 0 || rightListView.isDisabled());
moveUpButton.setDisable(rightSelectedIndex <= 0 || rightListView.isDisabled());
moveDownButton.setDisable(
rightSelectedIndex < 0
|| rightSelectedIndex >= rightItems.size() - 1
|| rightListView.isDisabled());
}
}

View File

@ -0,0 +1,14 @@
package p.studio.workspaces.assets.details.bank;
import p.studio.controls.banks.StudioDualListView;
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
public final class AssetDetailsBankCompositionDualListView extends StudioDualListView<AssetWorkspaceBankCompositionFile> {
@Override
protected String itemText(AssetWorkspaceBankCompositionFile item) {
if (item.displayName().equals(item.path())) {
return item.displayName();
}
return item.displayName() + " [" + item.path() + "]";
}
}