From 1f6df50f09fa18775d16c273510c9485230308d9 Mon Sep 17 00:00:00 2001 From: bQUARKz Date: Wed, 11 Mar 2026 19:01:57 +0000 Subject: [PATCH] added asset workspace working with packer --- .../java/p/studio/utilities/i18n/I18n.java | 1 + .../AssetNavigatorProjectionBuilder.java | 26 ++-- .../workspaces/assets/AssetWorkspace.java | 134 +++++++++++++++--- .../main/resources/i18n/messages.properties | 1 + .../resources/themes/default-prometeu.css | 23 +++ .../AssetNavigatorProjectionBuilderTest.java | 24 ++-- .../AssetWorkspacePreviewScaleTest.java | 28 ++++ .../main/assets/.prometeu/index.json | 9 ++ .../main/assets/ui/atlas-relocated/asset.json | 8 ++ .../ui/atlas-relocated/sprites/confirm.png | Bin 0 -> 137 bytes 10 files changed, 209 insertions(+), 45 deletions(-) create mode 100644 prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetWorkspacePreviewScaleTest.java create mode 100644 test-projects/main/assets/.prometeu/index.json create mode 100644 test-projects/main/assets/ui/atlas-relocated/asset.json create mode 100644 test-projects/main/assets/ui/atlas-relocated/sprites/confirm.png diff --git a/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java b/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java index f09b85fc..78c12b6b 100644 --- a/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java +++ b/prometeu-studio/src/main/java/p/studio/utilities/i18n/I18n.java @@ -145,6 +145,7 @@ public enum I18n { ASSETS_INPUTS_EMPTY("assets.inputs.empty"), ASSETS_DIAGNOSTICS_EMPTY("assets.diagnostics.empty"), ASSETS_PREVIEW_EMPTY("assets.preview.empty"), + ASSETS_PREVIEW_ZOOM("assets.preview.zoom"), ASSETS_PREVIEW_TEXT_ERROR("assets.preview.textError"), ASSETS_PREVIEW_IMAGE_ERROR("assets.preview.imageError"), ASSETS_PREVIEW_AUDIO_PLACEHOLDER("assets.preview.audioPlaceholder"), diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilder.java b/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilder.java index 1ab32323..978564cd 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilder.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilder.java @@ -9,11 +9,11 @@ public final class AssetNavigatorProjectionBuilder { public static AssetNavigatorProjection build( List assets, - Path assetsRoot, + Path projectRoot, String searchQuery, Set filters) { Objects.requireNonNull(assets, "assets"); - final Path normalizedAssetsRoot = Objects.requireNonNull(assetsRoot, "assetsRoot").toAbsolutePath().normalize(); + final Path normalizedProjectRoot = Objects.requireNonNull(projectRoot, "projectRoot").toAbsolutePath().normalize(); final String normalizedQuery = normalizeQuery(searchQuery); final Set normalizedFilters = filters == null || filters.isEmpty() ? EnumSet.noneOf(AssetNavigatorFilter.class) @@ -21,10 +21,10 @@ public final class AssetNavigatorProjectionBuilder { final Map> grouped = new LinkedHashMap<>(); for (AssetWorkspaceAssetSummary asset : assets) { - if (!matchesFilters(asset, normalizedFilters) || !matchesQuery(asset, normalizedAssetsRoot, normalizedQuery)) { + if (!matchesFilters(asset, normalizedFilters) || !matchesQuery(asset, normalizedProjectRoot, normalizedQuery)) { continue; } - grouped.computeIfAbsent(groupLabel(asset, normalizedAssetsRoot), ignored -> new ArrayList<>()) + grouped.computeIfAbsent(groupLabel(asset, normalizedProjectRoot), ignored -> new ArrayList<>()) .add(asset); } @@ -35,8 +35,8 @@ public final class AssetNavigatorProjectionBuilder { return new AssetNavigatorProjection(groups, visibleAssetCount); } - static String relativeRoot(AssetWorkspaceAssetSummary asset, Path assetsRoot) { - return relativize(asset.assetRoot(), assetsRoot).toString().replace('\\', '/'); + static String relativeRoot(AssetWorkspaceAssetSummary asset, Path projectRoot) { + return relativize(asset.assetRoot(), projectRoot).toString().replace('\\', '/'); } private static boolean matchesFilters(AssetWorkspaceAssetSummary asset, Set filters) { @@ -65,29 +65,29 @@ public final class AssetNavigatorProjectionBuilder { return true; } - private static boolean matchesQuery(AssetWorkspaceAssetSummary asset, Path assetsRoot, String normalizedQuery) { + private static boolean matchesQuery(AssetWorkspaceAssetSummary asset, Path projectRoot, String normalizedQuery) { if (normalizedQuery.isBlank()) { return true; } - final String relativeRoot = relativeRoot(asset, assetsRoot); + final String relativeRoot = relativeRoot(asset, projectRoot); return asset.assetName().toLowerCase(Locale.ROOT).contains(normalizedQuery) || asset.assetFamily().toLowerCase(Locale.ROOT).contains(normalizedQuery) || relativeRoot.toLowerCase(Locale.ROOT).contains(normalizedQuery); } - private static String groupLabel(AssetWorkspaceAssetSummary asset, Path assetsRoot) { - final Path relativeRoot = relativize(asset.assetRoot(), assetsRoot); + private static String groupLabel(AssetWorkspaceAssetSummary asset, Path projectRoot) { + final Path relativeRoot = relativize(asset.assetRoot(), projectRoot); final Path parent = relativeRoot.getParent(); if (parent == null) { - return "assets"; + return relativeRoot.toString().replace('\\', '/'); } return parent.toString().replace('\\', '/'); } - private static Path relativize(Path assetRoot, Path assetsRoot) { + private static Path relativize(Path assetRoot, Path projectRoot) { try { - return assetsRoot.relativize(assetRoot.toAbsolutePath().normalize()); + return projectRoot.relativize(assetRoot.toAbsolutePath().normalize()); } catch (IllegalArgumentException ignored) { return assetRoot.getFileName(); } diff --git a/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java b/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java index 1101fdac..cafe3896 100644 --- a/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java +++ b/prometeu-studio/src/main/java/p/studio/workspaces/assets/AssetWorkspace.java @@ -9,24 +9,20 @@ import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.*; +import p.packer.declarations.PackerAssetDeclarationParser; +import p.packer.foundation.PackerWorkspaceFoundation; +import p.packer.workspace.FileSystemPackerWorkspaceService; import p.studio.Container; import p.studio.events.*; import p.studio.projects.ProjectReference; import p.studio.utilities.i18n.I18n; import p.studio.workspaces.Workspace; import p.studio.workspaces.WorkspaceId; -import p.packer.declarations.PackerAssetDeclarationParser; -import p.packer.foundation.PackerWorkspaceFoundation; -import p.packer.workspace.FileSystemPackerWorkspaceService; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.CompletableFuture; public final class AssetWorkspace implements Workspace { @@ -46,6 +42,7 @@ public final class AssetWorkspace implements Workspace { private final VBox detailsContent = new VBox(12); private final Label workspaceSummaryLabel = new Label(); private final TextArea logsArea = new TextArea(); + private final ScrollPane detailsScroll = new ScrollPane(); private final Map filterButtons = new EnumMap<>(AssetNavigatorFilter.class); private final EnumSet activeFilters = EnumSet.noneOf(AssetNavigatorFilter.class); @@ -56,6 +53,7 @@ public final class AssetWorkspace implements Workspace { private volatile String detailsErrorMessage; private volatile AssetWorkspaceMutationPreview stagedMutationPreview; private volatile Path selectedPreviewInput; + private volatile int selectedPreviewZoom = 1; private String searchQuery = ""; public AssetWorkspace(ProjectReference projectReference) { @@ -167,7 +165,7 @@ public final class AssetWorkspace implements Workspace { detailsTitle.getStyleClass().add("assets-workspace-pane-title"); workspaceSummaryLabel.getStyleClass().add("assets-workspace-summary"); detailsContent.getStyleClass().add("assets-workspace-details-content"); - final ScrollPane detailsScroll = new ScrollPane(detailsContent); + detailsScroll.setContent(detailsContent); detailsScroll.setFitToWidth(true); detailsScroll.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); detailsScroll.getStyleClass().add("assets-workspace-details-scroll"); @@ -231,6 +229,7 @@ public final class AssetWorkspace implements Workspace { detailsErrorMessage = null; stagedMutationPreview = null; selectedPreviewInput = null; + selectedPreviewZoom = 1; setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_REFRESHING), ProgressBar.INDETERMINATE_PROGRESS, true); appendLog("Assets refresh started."); renderState(); @@ -268,6 +267,7 @@ public final class AssetWorkspace implements Workspace { detailsErrorMessage = null; stagedMutationPreview = null; selectedPreviewInput = null; + selectedPreviewZoom = 1; setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_LOADING_DETAILS), ProgressBar.INDETERMINATE_PROGRESS, true); appendLog("Loading details for " + selectionKey.stableKey() + "."); renderState(); @@ -291,6 +291,7 @@ public final class AssetWorkspace implements Workspace { selectedAssetDetails = details; detailsStatus = AssetWorkspaceDetailsStatus.READY; selectedPreviewInput = firstPreviewInput(details); + selectedPreviewZoom = 1; setInlineProgress(Container.i18n().text(I18n.ASSETS_PROGRESS_IDLE), 0, false); appendLog("Asset details ready for " + details.summary().assetName() + "."); renderState(); @@ -319,7 +320,7 @@ public final class AssetWorkspace implements Workspace { case READY -> { final AssetNavigatorProjection projection = AssetNavigatorProjectionBuilder.build( state.assets(), - assetsRoot(), + projectRoot(), searchQuery, activeFilters); if (projection.isEmpty()) { @@ -406,7 +407,7 @@ public final class AssetWorkspace implements Workspace { createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_STATE), summary.state().name().toLowerCase()), createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_ASSET_ID), summary.assetId() == null ? "—" : String.valueOf(summary.assetId())), createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_TYPE), summary.assetFamily()), - createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), summary.assetRoot().toString())); + createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), projectRelativePath(summary.assetRoot()))); return createSection(Container.i18n().text(I18n.ASSETS_SECTION_SUMMARY), content); } @@ -428,6 +429,7 @@ public final class AssetWorkspace implements Workspace { if (selectedPreviewInput == null || !containsInput(details, selectedPreviewInput)) { selectedPreviewInput = firstPreviewInput(details); + selectedPreviewZoom = 1; } final VBox inputsList = new VBox(8); @@ -445,6 +447,7 @@ public final class AssetWorkspace implements Workspace { inputButton.setMaxWidth(Double.MAX_VALUE); inputButton.setOnAction(event -> { selectedPreviewInput = input; + selectedPreviewZoom = 1; renderState(); }); roleBox.getChildren().add(inputButton); @@ -565,15 +568,18 @@ public final class AssetWorkspace implements Workspace { final String extension = extensionOf(input); if (isImage(extension)) { try { - final Image image = new Image(input.toUri().toString(), true); + final Image image = new Image(input.toUri().toString(), false); final ImageView imageView = new ImageView(image); imageView.setPreserveRatio(true); - imageView.setFitWidth(420); + imageView.setSmooth(false); + imageView.getStyleClass().add("assets-details-preview-image"); + previewBox.getChildren().add(createPreviewZoomBar(image)); + applyPreviewScale(image, imageView, selectedPreviewZoom); previewBox.getChildren().add(imageView); } catch (RuntimeException runtimeException) { previewBox.getChildren().add(createSectionMessage(Container.i18n().text(I18n.ASSETS_PREVIEW_IMAGE_ERROR))); } - previewBox.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), input.toString())); + previewBox.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), projectRelativePath(input))); return previewBox; } @@ -588,15 +594,78 @@ public final class AssetWorkspace implements Workspace { if (isAudio(extension)) { previewBox.getChildren().add(createSectionMessage(Container.i18n().format(I18n.ASSETS_PREVIEW_AUDIO_PLACEHOLDER, input.getFileName().toString()))); - previewBox.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), input.toString())); + previewBox.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), projectRelativePath(input))); return previewBox; } previewBox.getChildren().add(createSectionMessage(Container.i18n().format(I18n.ASSETS_PREVIEW_GENERIC_PLACEHOLDER, input.getFileName().toString()))); - previewBox.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), input.toString())); + previewBox.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), projectRelativePath(input))); return previewBox; } + private Node createPreviewZoomBar(Image image) { + final HBox zoomBar = new HBox(8); + zoomBar.setAlignment(Pos.CENTER_LEFT); + zoomBar.getStyleClass().add("assets-details-preview-zoom-bar"); + + final Label zoomLabel = new Label(Container.i18n().text(I18n.ASSETS_PREVIEW_ZOOM)); + zoomLabel.getStyleClass().add("assets-details-preview-zoom-label"); + zoomBar.getChildren().add(zoomLabel); + + final ToggleGroup zoomGroup = new ToggleGroup(); + final int maxZoom = maxPreviewZoom(image); + for (int zoom : List.of(1, 2, 4, 8)) { + final ToggleButton button = new ToggleButton("x" + zoom); + button.getStyleClass().add("assets-details-preview-zoom-button"); + button.setToggleGroup(zoomGroup); + button.setSelected(selectedPreviewZoom == zoom); + button.setDisable(zoom > maxZoom); + button.setOnAction(event -> { + selectedPreviewZoom = zoom; + renderState(); + }); + zoomBar.getChildren().add(button); + } + return zoomBar; + } + + private void applyPreviewScale(Image image, ImageView imageView, int requestedZoom) { + final double width = image.getWidth(); + final double height = image.getHeight(); + if (width <= 0.0d || height <= 0.0d) { + imageView.setFitWidth(420); + return; + } + + final double scale = previewScale(width, height, requestedZoom); + imageView.setFitWidth(Math.max(1.0d, width * scale)); + imageView.setFitHeight(Math.max(1.0d, height * scale)); + imageView.setSmooth(false); + } + + static double previewScale(double width, double height, int requestedZoom) { + final double longestEdge = Math.max(width, height); + if (longestEdge <= 0.0d) { + return 1.0d; + } + if (longestEdge > 420.0d) { + return 420.0d / longestEdge; + } + return Math.max(1, Math.min(Math.max(1, requestedZoom), maxPreviewZoom(width, height))); + } + + static int maxPreviewZoom(Image image) { + return maxPreviewZoom(image.getWidth(), image.getHeight()); + } + + static int maxPreviewZoom(double width, double height) { + final double longestEdge = Math.max(width, height); + if (longestEdge <= 0.0d || longestEdge > 420.0d) { + return 1; + } + return Math.max(1, (int) Math.floor(420.0d / longestEdge)); + } + private Node createSection(String title, Node content) { final VBox section = new VBox(10); section.getStyleClass().add("assets-details-section"); @@ -678,7 +747,7 @@ public final class AssetWorkspace implements Workspace { } topLine.getChildren().addAll(icon, name, spacer, badges); - final Label path = new Label(AssetNavigatorProjectionBuilder.relativeRoot(asset, assetsRoot())); + final Label path = new Label(AssetNavigatorProjectionBuilder.relativeRoot(asset, projectRoot())); path.getStyleClass().add("assets-workspace-asset-path"); row.getChildren().addAll(topLine, path); row.setOnMouseClicked(event -> selectAsset(asset.selectionKey())); @@ -722,10 +791,18 @@ public final class AssetWorkspace implements Workspace { if (selectedAsset == null) { return; } - final AssetWorkspaceMutationPreview preview = mutationService.preview(projectReference, selectedAsset, action); - stagedMutationPreview = preview; - appendLog("Preview ready for " + actionLabel(action) + "."); - renderState(); + try { + stagedMutationPreview = mutationService.preview(projectReference, selectedAsset, action); + appendLog("Preview ready for " + actionLabel(action) + "."); + renderState(); + Platform.runLater(() -> detailsScroll.setVvalue(1.0d)); + } catch (RuntimeException runtimeException) { + final String message = rootCauseMessage(runtimeException); + appendLog("Preview failed: " + message); + workspaceBus.publish(new StudioAssetsMutationFailedEvent(projectReference, action, message)); + stagedMutationPreview = null; + renderState(); + } } private Node createStagedMutationPanel(AssetWorkspaceMutationPreview preview) { @@ -786,7 +863,7 @@ public final class AssetWorkspace implements Workspace { box.getChildren().add(createKeyValueRow(Container.i18n().text(I18n.ASSETS_LABEL_NAME), preview.asset().assetName())); box.getChildren().add(createKeyValueRow( Container.i18n().text(I18n.ASSETS_LABEL_LOCATION), - AssetNavigatorProjectionBuilder.relativeRoot(preview.asset(), assetsRoot()))); + projectRelativePath(preview.asset().assetRoot()))); return box; } @@ -921,6 +998,19 @@ public final class AssetWorkspace implements Workspace { return projectReference.rootPath().resolve("assets").toAbsolutePath().normalize(); } + private Path projectRoot() { + return projectReference.rootPath().toAbsolutePath().normalize(); + } + + private String projectRelativePath(Path path) { + final Path normalizedPath = path.toAbsolutePath().normalize(); + try { + return projectRoot().relativize(normalizedPath).toString().replace('\\', '/'); + } catch (IllegalArgumentException ignored) { + return normalizedPath.toString().replace('\\', '/'); + } + } + private void setInlineProgress(String message, double progress, boolean visible) { inlineProgressLabel.setText(message); inlineProgressBar.setVisible(visible); diff --git a/prometeu-studio/src/main/resources/i18n/messages.properties b/prometeu-studio/src/main/resources/i18n/messages.properties index 0d974415..82aa3b60 100644 --- a/prometeu-studio/src/main/resources/i18n/messages.properties +++ b/prometeu-studio/src/main/resources/i18n/messages.properties @@ -135,6 +135,7 @@ assets.logs.title=Logs assets.inputs.empty=No previewable inputs are currently declared for this asset. assets.diagnostics.empty=No diagnostics are currently attached to this asset. assets.preview.empty=Select an input to preview it here. +assets.preview.zoom=Zoom assets.preview.textError=Unable to read this text-like input for preview. assets.preview.imageError=Unable to decode this image for preview. assets.preview.audioPlaceholder=Audio preview placeholder: {0} diff --git a/prometeu-studio/src/main/resources/themes/default-prometeu.css b/prometeu-studio/src/main/resources/themes/default-prometeu.css index db9a420e..1540c8a4 100644 --- a/prometeu-studio/src/main/resources/themes/default-prometeu.css +++ b/prometeu-studio/src/main/resources/themes/default-prometeu.css @@ -422,6 +422,29 @@ -fx-font-weight: bold; } +.assets-details-preview-zoom-bar { + -fx-alignment: center-left; +} + +.assets-details-preview-zoom-label { + -fx-text-fill: #9fc3e7; + -fx-font-size: 11px; + -fx-font-weight: bold; +} + +.assets-details-preview-zoom-button { + -fx-background-color: #17202a; + -fx-text-fill: #e6eff8; + -fx-background-radius: 999; + -fx-border-radius: 999; + -fx-border-color: #2f4053; +} + +.assets-details-preview-zoom-button:selected { + -fx-background-color: #224160; + -fx-border-color: #4f8dc3; +} + .assets-details-preview-text { -fx-control-inner-background: #10161d; -fx-text-fill: #e4edf6; diff --git a/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilderTest.java b/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilderTest.java index 5856a850..49f5660a 100644 --- a/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilderTest.java +++ b/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetNavigatorProjectionBuilderTest.java @@ -11,27 +11,29 @@ import static org.junit.jupiter.api.Assertions.assertEquals; final class AssetNavigatorProjectionBuilderTest { @Test void groupsAssetsByParentPath() { - final Path assetsRoot = Path.of("/tmp/project/assets"); + final Path projectRoot = Path.of("/tmp/project"); + final Path assetsRoot = projectRoot.resolve("assets"); final AssetNavigatorProjection projection = AssetNavigatorProjectionBuilder.build( List.of( managedAsset(1, "ui_atlas", "image_bank", assetsRoot.resolve("ui/atlas"), true, false), orphanAsset("menu_sounds", "sound_bank", assetsRoot.resolve("audio/menu"), false, false)), - assetsRoot, + projectRoot, "", EnumSet.noneOf(AssetNavigatorFilter.class)); assertEquals(2, projection.visibleAssetCount()); - assertEquals(List.of("audio", "ui"), projection.groups().stream().map(AssetNavigatorGroup::label).sorted().toList()); + assertEquals(List.of("assets/audio", "assets/ui"), projection.groups().stream().map(AssetNavigatorGroup::label).sorted().toList()); } @Test void managedAndOrphanFiltersBehaveAsStateFilterSet() { - final Path assetsRoot = Path.of("/tmp/project/assets"); + final Path projectRoot = Path.of("/tmp/project"); + final Path assetsRoot = projectRoot.resolve("assets"); final AssetNavigatorProjection projection = AssetNavigatorProjectionBuilder.build( List.of( managedAsset(1, "ui_atlas", "image_bank", assetsRoot.resolve("ui/atlas"), true, false), orphanAsset("menu_sounds", "sound_bank", assetsRoot.resolve("audio/menu"), false, false)), - assetsRoot, + projectRoot, "", EnumSet.of(AssetNavigatorFilter.MANAGED)); @@ -41,13 +43,14 @@ final class AssetNavigatorProjectionBuilderTest { @Test void diagnosticsAndPreloadActAsAdditionalConstraints() { - final Path assetsRoot = Path.of("/tmp/project/assets"); + final Path projectRoot = Path.of("/tmp/project"); + final Path assetsRoot = projectRoot.resolve("assets"); final AssetNavigatorProjection projection = AssetNavigatorProjectionBuilder.build( List.of( managedAsset(1, "ui_atlas", "image_bank", assetsRoot.resolve("ui/atlas"), true, true), managedAsset(2, "bg_tiles", "image_bank", assetsRoot.resolve("bg/tiles"), true, false), managedAsset(3, "voice_bank", "sound_bank", assetsRoot.resolve("audio/voice"), false, true)), - assetsRoot, + projectRoot, "", EnumSet.of(AssetNavigatorFilter.MANAGED, AssetNavigatorFilter.PRELOAD, AssetNavigatorFilter.DIAGNOSTICS)); @@ -57,19 +60,20 @@ final class AssetNavigatorProjectionBuilderTest { @Test void searchMatchesAssetNameAndPathContext() { - final Path assetsRoot = Path.of("/tmp/project/assets"); + final Path projectRoot = Path.of("/tmp/project"); + final Path assetsRoot = projectRoot.resolve("assets"); final List assets = List.of( managedAsset(1, "ui_atlas", "image_bank", assetsRoot.resolve("ui/atlas"), true, false), orphanAsset("menu_sounds", "sound_bank", assetsRoot.resolve("audio/menu"), false, false)); final AssetNavigatorProjection byName = AssetNavigatorProjectionBuilder.build( assets, - assetsRoot, + projectRoot, "atlas", EnumSet.noneOf(AssetNavigatorFilter.class)); final AssetNavigatorProjection byPath = AssetNavigatorProjectionBuilder.build( assets, - assetsRoot, + projectRoot, "audio", EnumSet.noneOf(AssetNavigatorFilter.class)); diff --git a/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetWorkspacePreviewScaleTest.java b/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetWorkspacePreviewScaleTest.java new file mode 100644 index 00000000..20403372 --- /dev/null +++ b/prometeu-studio/src/test/java/p/studio/workspaces/assets/AssetWorkspacePreviewScaleTest.java @@ -0,0 +1,28 @@ +package p.studio.workspaces.assets; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +final class AssetWorkspacePreviewScaleTest { + @Test + void keepsSmallImageAtOriginalScaleByDefault() { + assertEquals(1.0d, AssetWorkspace.previewScale(16.0d, 16.0d, 1)); + } + + @Test + void appliesIntegerZoomForSmallImages() { + assertEquals(8.0d, AssetWorkspace.previewScale(16.0d, 16.0d, 8)); + } + + @Test + void capsZoomOptionsByMaximumPreviewSize() { + assertEquals(6, AssetWorkspace.maxPreviewZoom(64.0d, 64.0d)); + assertEquals(1.0d, AssetWorkspace.previewScale(300.0d, 200.0d, 8)); + } + + @Test + void scalesLargeImagesDownToPreviewLimit() { + assertEquals(420.0d / 512.0d, AssetWorkspace.previewScale(512.0d, 128.0d, 1)); + } +} diff --git a/test-projects/main/assets/.prometeu/index.json b/test-projects/main/assets/.prometeu/index.json new file mode 100644 index 00000000..35f05e71 --- /dev/null +++ b/test-projects/main/assets/.prometeu/index.json @@ -0,0 +1,9 @@ +{ + "schema_version" : 1, + "next_asset_id" : 2, + "assets" : [ { + "asset_id" : 1, + "asset_uuid" : "67cd978d-cd61-4641-ba9e-98fe4bc039bd", + "root" : "ui/atlas-relocated" + } ] +} \ No newline at end of file diff --git a/test-projects/main/assets/ui/atlas-relocated/asset.json b/test-projects/main/assets/ui/atlas-relocated/asset.json new file mode 100644 index 00000000..e500ff6e --- /dev/null +++ b/test-projects/main/assets/ui/atlas-relocated/asset.json @@ -0,0 +1,8 @@ +{ + "schema_version": 1, + "name": "ui_atlas", + "type": "image_bank", + "inputs": { "sprites": ["sprites/confirm.png"] }, + "output": { "format": "TILES/indexed_v1", "codec": "RAW" }, + "preload": { "enabled": true } +} diff --git a/test-projects/main/assets/ui/atlas-relocated/sprites/confirm.png b/test-projects/main/assets/ui/atlas-relocated/sprites/confirm.png new file mode 100644 index 0000000000000000000000000000000000000000..1ee41b70ea12fcef7fedce1850f2693fab00806e GIT binary patch literal 137 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|oIPC}Lo9le z|NK4q#-5qAp;PE=h+#u(3U7`2%GXS{DjDytG$}rKK-!_m#K^$F;Lpz#KV@+N=}#8C i2iPaf?d4&!6Jyxi!KE&*nz9mT9D}E;pUXO@geCwXeJP3n literal 0 HcmV?d00001