refactor(rpgmaker): simplify button labels and enhance asset scanning
- Updated button labels for clarity: "Open folder" to "Open", "Decrypt highlighted" to "Decrypt", "Remove highlighted" to "Remove", and "Encrypt all + patch" to "Patch all". - Improved asset scanning logic to better handle editable images and ensure workspace images remain scannable until removed. - Added tests for new button functionalities and ensured proper behavior when no items are highlighted.
This commit is contained in:
parent
d82895cc4d
commit
7dee0b7f20
4 changed files with 100 additions and 24 deletions
|
|
@ -291,23 +291,23 @@ class RPGMakerImageManager(QWidget):
|
|||
|
||||
action_row = QHBoxLayout()
|
||||
action_row.setSpacing(8)
|
||||
self.open_workspace_button = QPushButton("Open folder")
|
||||
self.open_workspace_button = QPushButton("Open")
|
||||
self.open_workspace_button.setToolTip(
|
||||
"Open the highlighted image's editable folder, the chosen folder filter, or the "
|
||||
"editable img root."
|
||||
)
|
||||
self.open_workspace_button.clicked.connect(self._open_editable_folder)
|
||||
self.decrypt_selected_button = QPushButton("Decrypt highlighted")
|
||||
self.decrypt_selected_button = QPushButton("Decrypt")
|
||||
self.decrypt_selected_button.clicked.connect(self._decrypt_checked)
|
||||
self.decrypt_all_button = QPushButton("Decrypt all")
|
||||
self.decrypt_all_button.clicked.connect(self._decrypt_all)
|
||||
self.remove_button = QPushButton("Remove highlighted")
|
||||
self.remove_button = QPushButton("Remove")
|
||||
self.remove_button.setToolTip(
|
||||
"Delete highlighted PNG copies from the editable folder. Runtime images remain "
|
||||
"untouched and can be decrypted again. The Delete key does the same thing."
|
||||
)
|
||||
self.remove_button.clicked.connect(self._remove_highlighted)
|
||||
self.prepare_button = QPushButton("Encrypt all + patch")
|
||||
self.prepare_button = QPushButton("Patch all")
|
||||
self.prepare_button.setStyleSheet(
|
||||
"QPushButton{border:1px solid #4ec9b0;color:#4ec9b0;font-weight:bold;padding:6px 14px;}"
|
||||
"QPushButton:hover{background:#18352f;}"
|
||||
|
|
@ -494,9 +494,14 @@ class RPGMakerImageManager(QWidget):
|
|||
label = asset.relative_png.name
|
||||
item = QListWidgetItem(placeholder_icon, label)
|
||||
item.setData(_ASSET_ID_ROLE, asset.asset_id)
|
||||
kind = "encrypted + editable" if asset.has_encrypted and asset.has_plain else (
|
||||
"encrypted" if asset.has_encrypted else "runtime PNG"
|
||||
)
|
||||
if asset.has_encrypted and asset.has_plain:
|
||||
kind = "encrypted + editable"
|
||||
elif asset.has_encrypted:
|
||||
kind = "encrypted"
|
||||
elif asset.has_runtime_plain:
|
||||
kind = "runtime PNG"
|
||||
else:
|
||||
kind = "editable PNG"
|
||||
item.setToolTip(f"{asset.asset_id}\n{kind}")
|
||||
self.image_list.addItem(item)
|
||||
item.setSelected(asset.asset_id in self.selected_ids)
|
||||
|
|
@ -562,9 +567,9 @@ class RPGMakerImageManager(QWidget):
|
|||
|
||||
def _update_prepare_scope(self) -> None:
|
||||
if self.selected_ids:
|
||||
self.prepare_button.setText("Encrypt highlighted + patch")
|
||||
self.prepare_button.setText("Patch selected")
|
||||
else:
|
||||
self.prepare_button.setText("Encrypt all + patch")
|
||||
self.prepare_button.setText("Patch all")
|
||||
|
||||
def _show_preview(self, current: QListWidgetItem | None, _previous=None) -> None:
|
||||
if current is None:
|
||||
|
|
@ -606,14 +611,11 @@ class RPGMakerImageManager(QWidget):
|
|||
return
|
||||
try:
|
||||
workspace = ensure_editable_workspace(self.game_root)
|
||||
current = self.image_list.currentItem()
|
||||
asset = (
|
||||
self.assets_by_id.get(current.data(_ASSET_ID_ROLE))
|
||||
if current is not None
|
||||
else None
|
||||
)
|
||||
if asset is not None:
|
||||
target = asset.plain_path.parent
|
||||
highlighted_parents = {
|
||||
asset.plain_path.parent for asset in self._selected_assets()
|
||||
}
|
||||
if len(highlighted_parents) == 1:
|
||||
target = highlighted_parents.pop()
|
||||
else:
|
||||
root = Path(self.game_root).expanduser().resolve()
|
||||
content_relative = resolve_content_root(root).relative_to(root)
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ class RPGMakerImageManagerSelectionTests(unittest.TestCase):
|
|||
self.assertFalse(asset.plain_path.exists())
|
||||
self.assertTrue(asset.runtime_plain_path.exists())
|
||||
self.assertEqual(self.manager.image_list.count(), 0)
|
||||
self.assertEqual(self.manager.remove_button.text(), "Remove highlighted")
|
||||
self.assertEqual(self.manager.remove_button.text(), "Remove")
|
||||
|
||||
def test_prepare_uses_only_highlighted_editable_images(self):
|
||||
highlighted = self.manager.assets[0]
|
||||
|
|
@ -214,9 +214,7 @@ class RPGMakerImageManagerSelectionTests(unittest.TestCase):
|
|||
self.assertEqual(assets, [highlighted])
|
||||
self.assertTrue(highlighted.plain_path.exists())
|
||||
self.assertTrue(not_highlighted.plain_path.exists())
|
||||
self.assertEqual(
|
||||
self.manager.prepare_button.text(), "Encrypt highlighted + patch"
|
||||
)
|
||||
self.assertEqual(self.manager.prepare_button.text(), "Patch selected")
|
||||
|
||||
def test_prepare_uses_all_editable_images_without_highlights(self):
|
||||
editable = self.manager.assets[:2]
|
||||
|
|
@ -233,7 +231,7 @@ class RPGMakerImageManagerSelectionTests(unittest.TestCase):
|
|||
action, assets = start_action.call_args.args
|
||||
self.assertEqual(action, "prepare")
|
||||
self.assertEqual(assets, editable)
|
||||
self.assertEqual(self.manager.prepare_button.text(), "Encrypt all + patch")
|
||||
self.assertEqual(self.manager.prepare_button.text(), "Patch all")
|
||||
|
||||
def test_bottom_controls_share_one_row_and_action_width(self):
|
||||
action_buttons = (
|
||||
|
|
@ -256,6 +254,22 @@ class RPGMakerImageManagerSelectionTests(unittest.TestCase):
|
|||
self._click(0)
|
||||
asset_id = self.manager.image_list.currentItem().data(Qt.UserRole + 1)
|
||||
expected = self.manager.assets_by_id[asset_id].plain_path.parent
|
||||
self.manager.image_list.setCurrentItem(None)
|
||||
|
||||
with patch(
|
||||
"gui.rpgmaker_image_manager.QDesktopServices.openUrl",
|
||||
return_value=True,
|
||||
) as open_url:
|
||||
self.manager._open_editable_folder()
|
||||
|
||||
opened_url = open_url.call_args.args[0]
|
||||
self.assertEqual(Path(opened_url.toLocalFile()), expected)
|
||||
self.assertTrue(expected.is_dir())
|
||||
|
||||
def test_open_folder_ignores_current_item_without_highlights(self):
|
||||
self.manager.image_list.setCurrentRow(0)
|
||||
self.manager.selected_ids.clear()
|
||||
expected = self.game_root / ".dazedtl" / "images" / "img"
|
||||
|
||||
with patch(
|
||||
"gui.rpgmaker_image_manager.QDesktopServices.openUrl",
|
||||
|
|
|
|||
|
|
@ -88,6 +88,46 @@ class RPGMakerImageTests(unittest.TestCase):
|
|||
self.assertEqual(result.skipped, 1)
|
||||
self.assertEqual(plain.read_bytes(), translated)
|
||||
|
||||
def test_separate_decrypt_batches_accumulate_without_overwriting(self):
|
||||
self._encrypted_asset("001.rpgmvp", "red")
|
||||
self._encrypted_asset("002.rpgmvp", "green")
|
||||
first, second = scan_image_assets(self.root)
|
||||
|
||||
first_result = decrypt_assets([first], KEY, game_root=self.root)
|
||||
edited = png_bytes("blue")
|
||||
first.plain_path.write_bytes(edited)
|
||||
refreshed = {asset.asset_id: asset for asset in scan_image_assets(self.root)}
|
||||
second_result = decrypt_assets(
|
||||
[refreshed[second.asset_id]], KEY, game_root=self.root
|
||||
)
|
||||
|
||||
editable = {
|
||||
asset.asset_id: asset
|
||||
for asset in scan_image_assets(self.root)
|
||||
if asset.has_plain
|
||||
}
|
||||
self.assertEqual(first_result.completed, 1)
|
||||
self.assertEqual(second_result.completed, 1)
|
||||
self.assertEqual(set(editable), {first.asset_id, second.asset_id})
|
||||
self.assertEqual(editable[first.asset_id].plain_path.read_bytes(), edited)
|
||||
self.assertEqual(
|
||||
editable[second.asset_id].plain_path.read_bytes(), png_bytes("green")
|
||||
)
|
||||
|
||||
def test_workspace_image_remains_scannable_until_removed(self):
|
||||
encrypted = self._encrypted_asset()
|
||||
asset = scan_image_assets(self.root)[0]
|
||||
decrypt_assets([asset], KEY, game_root=self.root)
|
||||
encrypted.unlink()
|
||||
|
||||
editable_only = scan_image_assets(self.root)
|
||||
|
||||
self.assertEqual([item.asset_id for item in editable_only], [asset.asset_id])
|
||||
self.assertTrue(editable_only[0].has_plain)
|
||||
self.assertFalse(editable_only[0].has_encrypted)
|
||||
remove_editable_assets(self.root, editable_only)
|
||||
self.assertEqual(scan_image_assets(self.root), [])
|
||||
|
||||
def test_remove_editable_image_deletes_only_workspace_copy(self):
|
||||
self._encrypted_asset()
|
||||
asset = scan_image_assets(self.root)[0]
|
||||
|
|
|
|||
|
|
@ -122,11 +122,13 @@ def _logical_png(path: Path) -> Path:
|
|||
|
||||
|
||||
def scan_image_assets(game_root: str | Path) -> list[ImageAsset]:
|
||||
"""Scan ``img/`` and combine matching PNG/encrypted files into records."""
|
||||
"""Scan runtime and editable image trees and combine matching files."""
|
||||
|
||||
root = Path(game_root).expanduser().resolve()
|
||||
content_root = resolve_content_root(root)
|
||||
image_root = content_root / "img"
|
||||
workspace_root = editable_workspace_root(root)
|
||||
workspace_content_root = workspace_root / content_root.relative_to(root)
|
||||
by_id: dict[str, dict[str, Path]] = {}
|
||||
for path in image_root.rglob("*"):
|
||||
if not path.is_file():
|
||||
|
|
@ -149,11 +151,29 @@ def scan_image_assets(game_root: str | Path) -> list[ImageAsset]:
|
|||
else:
|
||||
entry["runtime_plain"] = path
|
||||
|
||||
editable_image_root = workspace_content_root / "img"
|
||||
if editable_image_root.is_dir():
|
||||
for path in editable_image_root.rglob("*"):
|
||||
if not path.is_file() or path.suffix.casefold() != ".png":
|
||||
continue
|
||||
relative = path.relative_to(workspace_content_root)
|
||||
asset_id = relative.as_posix()
|
||||
entry = by_id.setdefault(
|
||||
asset_id,
|
||||
{
|
||||
"relative": relative,
|
||||
"workspace_relative": path.relative_to(workspace_root),
|
||||
},
|
||||
)
|
||||
entry["plain"] = path
|
||||
|
||||
assets = [
|
||||
ImageAsset(
|
||||
asset_id=asset_id,
|
||||
relative_png=entry["relative"],
|
||||
plain_path=editable_workspace_root(root) / entry["workspace_relative"],
|
||||
plain_path=entry.get(
|
||||
"plain", workspace_root / entry["workspace_relative"]
|
||||
),
|
||||
encrypted_path=entry.get("encrypted"),
|
||||
runtime_plain_path=entry.get("runtime_plain"),
|
||||
)
|
||||
|
|
|
|||
Loading…
Reference in a new issue