implements PR-10d bank composition family policies and section coordinator
This commit is contained in:
parent
de8720abc3
commit
454f78d0fe
@ -0,0 +1,80 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetDetails;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
||||
abstract class AssetDetailsAbstractBankCompositionFamilySupport implements AssetDetailsBankCompositionFamilySupport {
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft createDraft(AssetWorkspaceAssetDetails details) {
|
||||
final List<AssetWorkspaceBankCompositionFile> selectedFiles = details.bankComposition().selectedFiles();
|
||||
final var selectedPaths = selectedFiles.stream().map(AssetWorkspaceBankCompositionFile::path).collect(java.util.stream.Collectors.toSet());
|
||||
final List<AssetWorkspaceBankCompositionFile> availableFiles = details.bankComposition().availableFiles().stream()
|
||||
.filter(file -> !selectedPaths.contains(file.path()))
|
||||
.toList();
|
||||
return new AssetDetailsBankCompositionDraft(availableFiles, selectedFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft moveToSelected(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final List<AssetWorkspaceBankCompositionFile> movableFiles = selectMovableFiles(draft, files);
|
||||
return draft.removeFromAvailable(movableFiles).appendToSelected(movableFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft moveToAvailable(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final var returnedFiles = new ArrayList<>(files);
|
||||
returnedFiles.sort(java.util.Comparator.comparing(AssetWorkspaceBankCompositionFile::path, String.CASE_INSENSITIVE_ORDER));
|
||||
return draft.removeFromSelected(files).appendToAvailable(returnedFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft moveUp(AssetDetailsBankCompositionDraft draft, int selectedIndex) {
|
||||
if (selectedIndex <= 0 || selectedIndex >= draft.selectedFiles().size()) {
|
||||
return draft;
|
||||
}
|
||||
final List<AssetWorkspaceBankCompositionFile> reordered = new ArrayList<>(draft.selectedFiles());
|
||||
final AssetWorkspaceBankCompositionFile moved = reordered.remove(selectedIndex);
|
||||
reordered.add(selectedIndex - 1, moved);
|
||||
return new AssetDetailsBankCompositionDraft(draft.availableFiles(), reordered);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft moveDown(AssetDetailsBankCompositionDraft draft, int selectedIndex) {
|
||||
if (selectedIndex < 0 || selectedIndex >= draft.selectedFiles().size() - 1) {
|
||||
return draft;
|
||||
}
|
||||
final List<AssetWorkspaceBankCompositionFile> reordered = new ArrayList<>(draft.selectedFiles());
|
||||
final AssetWorkspaceBankCompositionFile moved = reordered.remove(selectedIndex);
|
||||
reordered.add(selectedIndex + 1, moved);
|
||||
return new AssetDetailsBankCompositionDraft(draft.availableFiles(), reordered);
|
||||
}
|
||||
|
||||
protected abstract boolean canAdd(
|
||||
AssetDetailsBankCompositionDraft currentDraft,
|
||||
List<AssetWorkspaceBankCompositionFile> accepted,
|
||||
AssetWorkspaceBankCompositionFile candidate);
|
||||
|
||||
private List<AssetWorkspaceBankCompositionFile> selectMovableFiles(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final var requested = new LinkedHashSet<>(files);
|
||||
final List<AssetWorkspaceBankCompositionFile> movable = new ArrayList<>();
|
||||
for (AssetWorkspaceBankCompositionFile file : draft.availableFiles()) {
|
||||
if (!requested.contains(file)) {
|
||||
continue;
|
||||
}
|
||||
if (canAdd(draft, movable, file)) {
|
||||
movable.add(file);
|
||||
}
|
||||
}
|
||||
return List.copyOf(movable);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.studio.controls.banks.StudioAssetCapacitySeverity;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public record AssetDetailsBankCompositionCapacityState(
|
||||
double progress,
|
||||
StudioAssetCapacitySeverity severity,
|
||||
boolean blocked,
|
||||
String labelText,
|
||||
String hintText) {
|
||||
|
||||
public AssetDetailsBankCompositionCapacityState {
|
||||
progress = Math.max(0.0d, Math.min(1.0d, progress));
|
||||
severity = severity == null ? StudioAssetCapacitySeverity.GREEN : severity;
|
||||
labelText = Objects.requireNonNullElse(labelText, "");
|
||||
hintText = Objects.requireNonNullElse(hintText, "");
|
||||
}
|
||||
}
|
||||
@ -5,17 +5,13 @@ import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
import p.studio.Container;
|
||||
import p.studio.controls.banks.StudioAssetCapacityMeter;
|
||||
import p.studio.controls.banks.StudioAssetCapacitySeverity;
|
||||
import p.studio.controls.forms.StudioFormActionBar;
|
||||
import p.studio.controls.forms.StudioFormMode;
|
||||
import p.studio.controls.forms.StudioFormSession;
|
||||
import p.studio.controls.lifecycle.StudioControlLifecycle;
|
||||
import p.studio.controls.lifecycle.StudioControlLifecycleSupport;
|
||||
import p.studio.events.StudioWorkspaceEventBus;
|
||||
import p.studio.projects.ProjectReference;
|
||||
import p.studio.utilities.i18n.I18n;
|
||||
import p.studio.workspaces.assets.details.AssetDetailsUiSupport;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionDetails;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceDetailsViewState;
|
||||
import p.studio.workspaces.assets.messages.events.StudioAssetsDetailsViewStateChangedEvent;
|
||||
import p.studio.workspaces.framework.StudioSubscriptionBag;
|
||||
@ -32,9 +28,9 @@ public final class AssetDetailsBankCompositionControl extends VBox implements St
|
||||
this::cancelEdit);
|
||||
private final AssetDetailsBankCompositionDualListView dualListView = new AssetDetailsBankCompositionDualListView();
|
||||
private final StudioAssetCapacityMeter capacityMeter = new StudioAssetCapacityMeter();
|
||||
private final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
|
||||
|
||||
private AssetWorkspaceDetailsViewState viewState;
|
||||
private StudioFormSession<AssetWorkspaceBankCompositionDetails> formSession;
|
||||
|
||||
public AssetDetailsBankCompositionControl(ProjectReference projectReference, StudioWorkspaceEventBus workspaceBus) {
|
||||
StudioControlLifecycleSupport.install(this, this);
|
||||
@ -46,7 +42,7 @@ public final class AssetDetailsBankCompositionControl extends VBox implements St
|
||||
public void subscribe() {
|
||||
subscriptions.add(workspaceBus.subscribe(StudioAssetsDetailsViewStateChangedEvent.class, event -> {
|
||||
viewState = event.viewState();
|
||||
syncFormSession();
|
||||
coordinator.replaceDetails(viewState == null ? null : viewState.selectedAssetDetails());
|
||||
render();
|
||||
}));
|
||||
}
|
||||
@ -56,48 +52,46 @@ public final class AssetDetailsBankCompositionControl extends VBox implements St
|
||||
subscriptions.clear();
|
||||
}
|
||||
|
||||
private void syncFormSession() {
|
||||
if (viewState == null || viewState.selectedAssetDetails() == null) {
|
||||
formSession = null;
|
||||
return;
|
||||
}
|
||||
|
||||
final AssetWorkspaceBankCompositionDetails source = viewState.selectedAssetDetails().bankComposition();
|
||||
if (formSession == null) {
|
||||
formSession = new StudioFormSession<>(source);
|
||||
return;
|
||||
}
|
||||
if (!Objects.equals(formSession.source(), source)) {
|
||||
formSession.replaceSource(source);
|
||||
}
|
||||
}
|
||||
|
||||
private void render() {
|
||||
if (viewState == null || viewState.selectedAssetDetails() == null) {
|
||||
getChildren().clear();
|
||||
return;
|
||||
}
|
||||
if (formSession == null) {
|
||||
syncFormSession();
|
||||
}
|
||||
if (formSession == null) {
|
||||
if (!coordinator.ready()) {
|
||||
getChildren().clear();
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean editing = formSession.mode() == StudioFormMode.EDITING;
|
||||
final AssetWorkspaceBankCompositionDetails draft = formSession.draft();
|
||||
final AssetDetailsBankCompositionViewModel viewModel = coordinator.viewModel();
|
||||
|
||||
dualListView.setLeftItems(draft.availableFiles());
|
||||
dualListView.setRightItems(draft.selectedFiles());
|
||||
dualListView.setInteractionEnabled(false);
|
||||
dualListView.setLeftItems(viewModel.availableFiles());
|
||||
dualListView.setRightItems(viewModel.selectedFiles());
|
||||
dualListView.setInteractionEnabled(viewModel.editing());
|
||||
dualListView.setOnMoveToRight(items -> {
|
||||
coordinator.moveToSelected(items);
|
||||
render();
|
||||
});
|
||||
dualListView.setOnMoveToLeft(items -> {
|
||||
coordinator.moveToAvailable(items);
|
||||
render();
|
||||
});
|
||||
dualListView.setOnMoveUp(index -> {
|
||||
coordinator.moveUp(index);
|
||||
render();
|
||||
});
|
||||
dualListView.setOnMoveDown(index -> {
|
||||
coordinator.moveDown(index);
|
||||
render();
|
||||
});
|
||||
|
||||
capacityMeter.setProgress(0.0d);
|
||||
capacityMeter.setSeverity(StudioAssetCapacitySeverity.GREEN);
|
||||
capacityMeter.setLabelText(draft.measuredBankSizeBytes() + " bytes");
|
||||
capacityMeter.setHintText(editing
|
||||
? Container.i18n().text(I18n.ASSETS_DETAILS_BANK_COMPOSITION_EDITING_HINT)
|
||||
: Container.i18n().text(I18n.ASSETS_DETAILS_BANK_COMPOSITION_READONLY_HINT));
|
||||
capacityMeter.setProgress(viewModel.capacityState().progress());
|
||||
capacityMeter.setSeverity(viewModel.capacityState().severity());
|
||||
capacityMeter.setLabelText(viewModel.capacityState().labelText());
|
||||
capacityMeter.setHintText(viewModel.capacityState().hintText().isBlank()
|
||||
? (viewModel.editing()
|
||||
? Container.i18n().text(I18n.ASSETS_DETAILS_BANK_COMPOSITION_EDITING_HINT)
|
||||
: Container.i18n().text(I18n.ASSETS_DETAILS_BANK_COMPOSITION_READONLY_HINT))
|
||||
: viewModel.capacityState().hintText());
|
||||
|
||||
final HBox body = new HBox(16, dualListView, capacityMeter);
|
||||
body.getStyleClass().add("assets-details-bank-composition-body");
|
||||
@ -105,7 +99,7 @@ public final class AssetDetailsBankCompositionControl extends VBox implements St
|
||||
dualListView.setMaxWidth(Double.MAX_VALUE);
|
||||
|
||||
final VBox content = new VBox(12, body);
|
||||
actionBar.updateState(formSession.mode(), formSession.isDirty());
|
||||
actionBar.updateState(viewModel.editing() ? p.studio.controls.forms.StudioFormMode.EDITING : p.studio.controls.forms.StudioFormMode.READ_ONLY, viewModel.dirty());
|
||||
content.getChildren().add(actionBar);
|
||||
|
||||
final VBox section = AssetDetailsUiSupport.createSection(
|
||||
@ -116,34 +110,22 @@ public final class AssetDetailsBankCompositionControl extends VBox implements St
|
||||
}
|
||||
|
||||
private void beginEdit() {
|
||||
if (formSession == null) {
|
||||
return;
|
||||
}
|
||||
formSession.beginEdit();
|
||||
coordinator.beginEdit();
|
||||
render();
|
||||
}
|
||||
|
||||
private void applyDraft() {
|
||||
if (formSession == null) {
|
||||
return;
|
||||
}
|
||||
formSession.apply();
|
||||
coordinator.apply();
|
||||
render();
|
||||
}
|
||||
|
||||
private void resetDraft() {
|
||||
if (formSession == null) {
|
||||
return;
|
||||
}
|
||||
formSession.resetDraft();
|
||||
coordinator.reset();
|
||||
render();
|
||||
}
|
||||
|
||||
private void cancelEdit() {
|
||||
if (formSession == null) {
|
||||
return;
|
||||
}
|
||||
formSession.cancelEdit();
|
||||
coordinator.cancel();
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,115 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.studio.controls.forms.StudioFormMode;
|
||||
import p.studio.controls.forms.StudioFormSession;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetDetails;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public final class AssetDetailsBankCompositionCoordinator {
|
||||
private final List<AssetDetailsBankCompositionFamilySupport> familySupports = List.of(
|
||||
new AssetDetailsTileBankCompositionFamilySupport(),
|
||||
new AssetDetailsSoundBankCompositionFamilySupport(),
|
||||
new AssetDetailsFallbackBankCompositionFamilySupport());
|
||||
|
||||
private AssetDetailsBankCompositionFamilySupport familySupport;
|
||||
private StudioFormSession<AssetDetailsBankCompositionDraft> formSession;
|
||||
|
||||
public void replaceDetails(AssetWorkspaceAssetDetails details) {
|
||||
if (details == null) {
|
||||
familySupport = null;
|
||||
formSession = null;
|
||||
return;
|
||||
}
|
||||
|
||||
familySupport = familySupports.stream()
|
||||
.filter(candidate -> candidate.supports(details.summary().assetFamily()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
final AssetDetailsBankCompositionDraft source = familySupport.createDraft(details);
|
||||
if (formSession == null) {
|
||||
formSession = new StudioFormSession<>(source);
|
||||
return;
|
||||
}
|
||||
if (!Objects.equals(formSession.source(), source)) {
|
||||
formSession.replaceSource(source);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean ready() {
|
||||
return formSession != null && familySupport != null;
|
||||
}
|
||||
|
||||
public void beginEdit() {
|
||||
if (ready()) {
|
||||
formSession.beginEdit();
|
||||
}
|
||||
}
|
||||
|
||||
public void apply() {
|
||||
if (ready()) {
|
||||
formSession.apply();
|
||||
}
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
if (ready()) {
|
||||
formSession.resetDraft();
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
if (ready()) {
|
||||
formSession.cancelEdit();
|
||||
}
|
||||
}
|
||||
|
||||
public void moveToSelected(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
if (!ready() || formSession.mode() != StudioFormMode.EDITING) {
|
||||
return;
|
||||
}
|
||||
formSession.updateDraft(current -> familySupport.moveToSelected(current, files));
|
||||
}
|
||||
|
||||
public void moveToAvailable(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
if (!ready() || formSession.mode() != StudioFormMode.EDITING) {
|
||||
return;
|
||||
}
|
||||
formSession.updateDraft(current -> familySupport.moveToAvailable(current, files));
|
||||
}
|
||||
|
||||
public void moveUp(int selectedIndex) {
|
||||
if (!ready() || formSession.mode() != StudioFormMode.EDITING) {
|
||||
return;
|
||||
}
|
||||
formSession.updateDraft(current -> familySupport.moveUp(current, selectedIndex));
|
||||
}
|
||||
|
||||
public void moveDown(int selectedIndex) {
|
||||
if (!ready() || formSession.mode() != StudioFormMode.EDITING) {
|
||||
return;
|
||||
}
|
||||
formSession.updateDraft(current -> familySupport.moveDown(current, selectedIndex));
|
||||
}
|
||||
|
||||
public AssetDetailsBankCompositionViewModel viewModel() {
|
||||
if (!ready()) {
|
||||
return new AssetDetailsBankCompositionViewModel(
|
||||
false,
|
||||
false,
|
||||
List.of(),
|
||||
List.of(),
|
||||
new AssetDetailsBankCompositionCapacityState(0.0d, null, false, "", ""));
|
||||
}
|
||||
|
||||
final AssetDetailsBankCompositionDraft draft = formSession.draft();
|
||||
return new AssetDetailsBankCompositionViewModel(
|
||||
formSession.mode() == StudioFormMode.EDITING,
|
||||
formSession.isDirty(),
|
||||
draft.availableFiles(),
|
||||
draft.selectedFiles(),
|
||||
familySupport.evaluate(draft));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record AssetDetailsBankCompositionDraft(
|
||||
List<AssetWorkspaceBankCompositionFile> availableFiles,
|
||||
List<AssetWorkspaceBankCompositionFile> selectedFiles) {
|
||||
|
||||
public AssetDetailsBankCompositionDraft {
|
||||
availableFiles = List.copyOf(Objects.requireNonNull(availableFiles, "availableFiles"));
|
||||
selectedFiles = List.copyOf(Objects.requireNonNull(selectedFiles, "selectedFiles"));
|
||||
}
|
||||
|
||||
public AssetDetailsBankCompositionDraft removeFromAvailable(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final var removed = new LinkedHashSet<>(Objects.requireNonNull(files, "files"));
|
||||
return new AssetDetailsBankCompositionDraft(
|
||||
availableFiles.stream().filter(file -> !removed.contains(file)).toList(),
|
||||
selectedFiles);
|
||||
}
|
||||
|
||||
public AssetDetailsBankCompositionDraft appendToSelected(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final var nextSelected = new java.util.ArrayList<>(selectedFiles);
|
||||
nextSelected.addAll(Objects.requireNonNull(files, "files"));
|
||||
return new AssetDetailsBankCompositionDraft(availableFiles, nextSelected);
|
||||
}
|
||||
|
||||
public AssetDetailsBankCompositionDraft removeFromSelected(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final var removed = new LinkedHashSet<>(Objects.requireNonNull(files, "files"));
|
||||
return new AssetDetailsBankCompositionDraft(
|
||||
availableFiles,
|
||||
selectedFiles.stream().filter(file -> !removed.contains(file)).toList());
|
||||
}
|
||||
|
||||
public AssetDetailsBankCompositionDraft appendToAvailable(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
final var nextAvailable = new java.util.ArrayList<>(availableFiles);
|
||||
nextAvailable.addAll(Objects.requireNonNull(files, "files"));
|
||||
return new AssetDetailsBankCompositionDraft(nextAvailable, selectedFiles);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetDetails;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
interface AssetDetailsBankCompositionFamilySupport {
|
||||
boolean supports(AssetFamilyCatalog assetFamily);
|
||||
|
||||
AssetDetailsBankCompositionDraft createDraft(AssetWorkspaceAssetDetails details);
|
||||
|
||||
AssetDetailsBankCompositionDraft moveToSelected(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
List<AssetWorkspaceBankCompositionFile> files);
|
||||
|
||||
AssetDetailsBankCompositionDraft moveToAvailable(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
List<AssetWorkspaceBankCompositionFile> files);
|
||||
|
||||
AssetDetailsBankCompositionDraft moveUp(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
int selectedIndex);
|
||||
|
||||
AssetDetailsBankCompositionDraft moveDown(
|
||||
AssetDetailsBankCompositionDraft draft,
|
||||
int selectedIndex);
|
||||
|
||||
AssetDetailsBankCompositionCapacityState evaluate(AssetDetailsBankCompositionDraft draft);
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public record AssetDetailsBankCompositionViewModel(
|
||||
boolean editing,
|
||||
boolean dirty,
|
||||
List<AssetWorkspaceBankCompositionFile> availableFiles,
|
||||
List<AssetWorkspaceBankCompositionFile> selectedFiles,
|
||||
AssetDetailsBankCompositionCapacityState capacityState) {
|
||||
|
||||
public AssetDetailsBankCompositionViewModel {
|
||||
availableFiles = List.copyOf(Objects.requireNonNull(availableFiles, "availableFiles"));
|
||||
selectedFiles = List.copyOf(Objects.requireNonNull(selectedFiles, "selectedFiles"));
|
||||
capacityState = Objects.requireNonNull(capacityState, "capacityState");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.studio.controls.banks.StudioAssetCapacitySeverity;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class AssetDetailsFallbackBankCompositionFamilySupport extends AssetDetailsAbstractBankCompositionFamilySupport {
|
||||
@Override
|
||||
public boolean supports(AssetFamilyCatalog assetFamily) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canAdd(
|
||||
AssetDetailsBankCompositionDraft currentDraft,
|
||||
List<AssetWorkspaceBankCompositionFile> accepted,
|
||||
AssetWorkspaceBankCompositionFile candidate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionCapacityState evaluate(AssetDetailsBankCompositionDraft draft) {
|
||||
return new AssetDetailsBankCompositionCapacityState(
|
||||
0.0d,
|
||||
StudioAssetCapacitySeverity.GREEN,
|
||||
false,
|
||||
draft.selectedFiles().size() + " selected",
|
||||
"");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.studio.controls.banks.StudioAssetCapacitySeverity;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class AssetDetailsSoundBankCompositionFamilySupport extends AssetDetailsAbstractBankCompositionFamilySupport {
|
||||
private static final long MAX_BYTES = 1024L * 1024L;
|
||||
|
||||
@Override
|
||||
public boolean supports(AssetFamilyCatalog assetFamily) {
|
||||
return assetFamily == AssetFamilyCatalog.SOUND_BANK;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canAdd(
|
||||
AssetDetailsBankCompositionDraft currentDraft,
|
||||
List<AssetWorkspaceBankCompositionFile> accepted,
|
||||
AssetWorkspaceBankCompositionFile candidate) {
|
||||
final long usedBytes = selectedBytes(currentDraft.selectedFiles()) + selectedBytes(accepted);
|
||||
return usedBytes + candidate.size() <= MAX_BYTES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionCapacityState evaluate(AssetDetailsBankCompositionDraft draft) {
|
||||
final long usedBytes = selectedBytes(draft.selectedFiles());
|
||||
final double progress = (double) usedBytes / (double) MAX_BYTES;
|
||||
final boolean blocked = usedBytes >= MAX_BYTES;
|
||||
return new AssetDetailsBankCompositionCapacityState(
|
||||
progress,
|
||||
severityFor(progress),
|
||||
blocked,
|
||||
usedBytes + " / " + MAX_BYTES + " bytes",
|
||||
blocked ? "Sound bank byte capacity reached." : "");
|
||||
}
|
||||
|
||||
private long selectedBytes(List<AssetWorkspaceBankCompositionFile> files) {
|
||||
return files.stream().mapToLong(AssetWorkspaceBankCompositionFile::size).sum();
|
||||
}
|
||||
|
||||
private StudioAssetCapacitySeverity severityFor(double progress) {
|
||||
if (progress >= 0.95d) {
|
||||
return StudioAssetCapacitySeverity.RED;
|
||||
}
|
||||
if (progress >= 0.65d) {
|
||||
return StudioAssetCapacitySeverity.ORANGE;
|
||||
}
|
||||
return StudioAssetCapacitySeverity.GREEN;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.studio.controls.banks.StudioAssetCapacitySeverity;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceAssetDetails;
|
||||
import p.studio.workspaces.assets.messages.AssetWorkspaceBankCompositionFile;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
final class AssetDetailsTileBankCompositionFamilySupport extends AssetDetailsAbstractBankCompositionFamilySupport {
|
||||
private int maxSlots = 64;
|
||||
|
||||
@Override
|
||||
public boolean supports(AssetFamilyCatalog assetFamily) {
|
||||
return assetFamily == AssetFamilyCatalog.TILE_BANK;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionDraft createDraft(AssetWorkspaceAssetDetails details) {
|
||||
maxSlots = computeMaxSlots(details);
|
||||
return super.createDraft(details);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean canAdd(
|
||||
AssetDetailsBankCompositionDraft currentDraft,
|
||||
List<AssetWorkspaceBankCompositionFile> accepted,
|
||||
AssetWorkspaceBankCompositionFile candidate) {
|
||||
return currentDraft.selectedFiles().size() + accepted.size() < maxSlots;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AssetDetailsBankCompositionCapacityState evaluate(AssetDetailsBankCompositionDraft draft) {
|
||||
final int used = draft.selectedFiles().size();
|
||||
final double progress = maxSlots <= 0 ? 0.0d : (double) used / (double) maxSlots;
|
||||
final boolean blocked = used >= maxSlots;
|
||||
return new AssetDetailsBankCompositionCapacityState(
|
||||
progress,
|
||||
severityFor(progress),
|
||||
blocked,
|
||||
used + " / " + maxSlots,
|
||||
blocked ? "Tile bank slot capacity reached." : "");
|
||||
}
|
||||
|
||||
private int computeMaxSlots(AssetWorkspaceAssetDetails details) {
|
||||
final String tileSizeValue = details.metadataFields().stream()
|
||||
.filter(field -> field.key().equals("tile_size"))
|
||||
.map(field -> field.value())
|
||||
.findFirst()
|
||||
.orElse("16x16");
|
||||
final int tileSize = switch (tileSizeValue) {
|
||||
case "8x8" -> 8;
|
||||
case "32x32" -> 32;
|
||||
default -> 16;
|
||||
};
|
||||
return Math.max(1, (256 / tileSize) * (256 / tileSize));
|
||||
}
|
||||
|
||||
private StudioAssetCapacitySeverity severityFor(double progress) {
|
||||
if (progress >= 0.95d) {
|
||||
return StudioAssetCapacitySeverity.RED;
|
||||
}
|
||||
if (progress >= 0.65d) {
|
||||
return StudioAssetCapacitySeverity.ORANGE;
|
||||
}
|
||||
return StudioAssetCapacitySeverity.GREEN;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,121 @@
|
||||
package p.studio.workspaces.assets.details.bank;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import p.packer.dtos.PackerCodecConfigurationFieldDTO;
|
||||
import p.packer.messages.AssetReference;
|
||||
import p.packer.messages.assets.AssetFamilyCatalog;
|
||||
import p.packer.messages.assets.OutputCodecCatalog;
|
||||
import p.packer.messages.assets.OutputFormatCatalog;
|
||||
import p.packer.messages.assets.PackerCodecConfigurationFieldType;
|
||||
import p.studio.workspaces.assets.messages.*;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
final class AssetDetailsBankCompositionCoordinatorTest {
|
||||
@Test
|
||||
void tileFamilyBlocksSelectionAfterSlotCapacity() {
|
||||
final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
|
||||
coordinator.replaceDetails(tileDetails("32x32", 65));
|
||||
coordinator.beginEdit();
|
||||
|
||||
coordinator.moveToSelected(coordinator.viewModel().availableFiles());
|
||||
|
||||
assertEquals(64, coordinator.viewModel().selectedFiles().size());
|
||||
assertTrue(coordinator.viewModel().capacityState().blocked());
|
||||
}
|
||||
|
||||
@Test
|
||||
void soundFamilyBlocksSelectionAfterByteCapacity() {
|
||||
final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
|
||||
coordinator.replaceDetails(soundDetails(600_000L, 600_000L));
|
||||
coordinator.beginEdit();
|
||||
|
||||
coordinator.moveToSelected(coordinator.viewModel().availableFiles());
|
||||
|
||||
assertEquals(1, coordinator.viewModel().selectedFiles().size());
|
||||
assertTrue(coordinator.viewModel().capacityState().progress() > 0.5d);
|
||||
}
|
||||
|
||||
@Test
|
||||
void resetAndCancelFollowStudioFormSessionLifecycle() {
|
||||
final AssetDetailsBankCompositionCoordinator coordinator = new AssetDetailsBankCompositionCoordinator();
|
||||
coordinator.replaceDetails(tileDetails("16x16", 2));
|
||||
coordinator.beginEdit();
|
||||
|
||||
final var firstFile = coordinator.viewModel().availableFiles().subList(0, 1);
|
||||
coordinator.moveToSelected(firstFile);
|
||||
assertTrue(coordinator.viewModel().dirty());
|
||||
|
||||
coordinator.reset();
|
||||
assertFalse(coordinator.viewModel().dirty());
|
||||
|
||||
coordinator.moveToSelected(firstFile);
|
||||
assertTrue(coordinator.viewModel().dirty());
|
||||
coordinator.cancel();
|
||||
assertFalse(coordinator.viewModel().editing());
|
||||
assertFalse(coordinator.viewModel().dirty());
|
||||
}
|
||||
|
||||
private AssetWorkspaceAssetDetails tileDetails(String tileSize, int fileCount) {
|
||||
return new AssetWorkspaceAssetDetails(
|
||||
summary(AssetFamilyCatalog.TILE_BANK),
|
||||
List.of(),
|
||||
OutputFormatCatalog.TILES_INDEXED_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
List.of(OutputCodecCatalog.NONE),
|
||||
Map.of(OutputCodecCatalog.NONE, List.of()),
|
||||
List.of(new PackerCodecConfigurationFieldDTO("tile_size", "Tile Size", PackerCodecConfigurationFieldType.TEXT, tileSize, true, List.of())),
|
||||
new AssetWorkspaceBankCompositionDetails(
|
||||
files("tile", fileCount, 1024L),
|
||||
List.of(),
|
||||
0L),
|
||||
Map.of());
|
||||
}
|
||||
|
||||
private AssetWorkspaceAssetDetails soundDetails(long firstSize, long secondSize) {
|
||||
return new AssetWorkspaceAssetDetails(
|
||||
summary(AssetFamilyCatalog.SOUND_BANK),
|
||||
List.of(),
|
||||
OutputFormatCatalog.SOUND_V1,
|
||||
OutputCodecCatalog.NONE,
|
||||
List.of(OutputCodecCatalog.NONE),
|
||||
Map.of(OutputCodecCatalog.NONE, List.of()),
|
||||
List.of(),
|
||||
new AssetWorkspaceBankCompositionDetails(
|
||||
List.of(
|
||||
new AssetWorkspaceBankCompositionFile("a.wav", "a.wav", firstSize, 1L, null, Map.of()),
|
||||
new AssetWorkspaceBankCompositionFile("b.wav", "b.wav", secondSize, 1L, null, Map.of())),
|
||||
List.of(),
|
||||
0L),
|
||||
Map.of());
|
||||
}
|
||||
|
||||
private List<AssetWorkspaceBankCompositionFile> files(String prefix, int count, long size) {
|
||||
return java.util.stream.IntStream.range(0, count)
|
||||
.mapToObj(index -> new AssetWorkspaceBankCompositionFile(
|
||||
prefix + "-" + index + ".png",
|
||||
prefix + "-" + index + ".png",
|
||||
size,
|
||||
1L,
|
||||
null,
|
||||
Map.of()))
|
||||
.toList();
|
||||
}
|
||||
|
||||
private AssetWorkspaceAssetSummary summary(AssetFamilyCatalog family) {
|
||||
return new AssetWorkspaceAssetSummary(
|
||||
AssetReference.forAssetId(1),
|
||||
"bank",
|
||||
AssetWorkspaceAssetState.REGISTERED,
|
||||
AssetWorkspaceBuildParticipation.INCLUDED,
|
||||
1,
|
||||
family,
|
||||
Path.of("/tmp/bank"),
|
||||
false,
|
||||
false);
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user