From 637370dfc9d5218f0b0dcfc59ac01b30ee05a9b8 Mon Sep 17 00:00:00 2001 From: DazedAnon Date: Thu, 7 May 2026 14:00:28 -0500 Subject: [PATCH] Improve the look a file selector a bit more. --- gui/workflow_tab.py | 96 ++++++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 28 deletions(-) diff --git a/gui/workflow_tab.py b/gui/workflow_tab.py index 8d2e183..e4500e5 100644 --- a/gui/workflow_tab.py +++ b/gui/workflow_tab.py @@ -431,18 +431,20 @@ def _make_btn(text: str, color: str = "#007acc") -> QPushButton: return btn -def _make_toggle_btn(text: str) -> QPushButton: - """Checkable Select All / Deselect All button, styled to match the rest of the workflow UI.""" - btn = QPushButton(text) - btn.setCheckable(True) +def _make_icon_btn(icon_text: str, tooltip: str = "") -> QPushButton: + """Compact icon-only button matching the Translation tab action buttons.""" + btn = QPushButton(icon_text) + btn.setToolTip(tooltip) + btn.setFont(QFont("Segoe UI", 12)) + btn.setFixedSize(40, 36) btn.setStyleSheet( - "QPushButton{background-color:#2d2d30;color:#aaaaaa;" - "border:1px solid #555555;padding:4px 12px;" - "border-radius:4px;font-size:12px;" - "font-family:'Segoe UI','Segoe UI Emoji','Apple Color Emoji',sans-serif;}" - "QPushButton:hover{background-color:#3e3e42;color:#cccccc;}" - "QPushButton:checked{background-color:#1a3a5a;color:#7ab8d4;border-color:#2a6a9a;}" - "QPushButton:checked:hover{background-color:#1e4268;}" + "QPushButton{background-color:#2d2d30;color:white;" + "font-weight:bold;font-size:16px;border:1px solid #555555;" + "border-radius:4px;min-width:40px;max-width:40px;" + "min-height:36px;max-height:36px;}" + "QPushButton:hover{background-color:#3e3e42;border-left-color:#007acc;}" + "QPushButton:pressed{background-color:#007acc;}" + "QPushButton:disabled{background-color:#2d2d30;color:#555555;border-color:#444444;}" ) return btn @@ -489,6 +491,9 @@ class WorkflowTab(QWidget): self._p2_auto_apply_timer: QTimer | None = None self._syncing_file_checks: bool = False self._import_buttons: list[QPushButton] = [] + self._current_step_index: int = 0 + self._last_import_signature: tuple[str, ...] | None = None + self._pending_import_signature: tuple[str, ...] | None = None self._init_ui() @@ -701,6 +706,12 @@ class WorkflowTab(QWidget): def _on_step_tab_changed(self, index: int): """Refresh config-backed controls when their workflow page is shown.""" + previous_index = self._current_step_index + self._current_step_index = index + + if previous_index == 0 and index != 0: + self._auto_import_if_needed() + if index == 5: self._populate_p2_checkboxes() @@ -791,8 +802,7 @@ class WorkflowTab(QWidget): row0.addWidget(self.folder_edit, 1) self.folder_edit.returnPressed.connect(self._detect_folder) - browse_btn = _make_btn(" Browse…") - browse_btn.setIcon(browse_btn.style().standardIcon(QStyle.SP_DirOpenIcon)) + browse_btn = _make_icon_btn("πŸ“", "Browse for a game project folder") browse_btn.clicked.connect(self._browse_folder) row0.addWidget(browse_btn) @@ -819,6 +829,7 @@ class WorkflowTab(QWidget): "background-color:#252526;border-radius:4px;}" "QListWidget::item{border:none;outline:none;padding:2px 6px;" "color:#c8c8c8;}" + "QListWidget::item:selected{background-color:#252526;color:#c8c8c8;}" "QListWidget::item:hover{background-color:#2d2d30;" "border-left:2px solid #007acc;}" ) @@ -827,27 +838,23 @@ class WorkflowTab(QWidget): # Action row row1 = QHBoxLayout() row1.setSpacing(6) - self.sel_all_btn = _make_btn("Select All", "#555") - self.sel_all_btn.setFixedWidth(110) - self.sel_all_btn.clicked.connect(self._select_all_files) - row1.addWidget(self.sel_all_btn) + select_all_btn = _make_icon_btn("βœ“", "Select all importable files") + select_all_btn.clicked.connect(self._select_all_files) + row1.addWidget(select_all_btn) - deselect_all_btn = _make_btn("Deselect All", "#555") - deselect_all_btn.setFixedWidth(110) + deselect_all_btn = _make_icon_btn("βœ—", "Deselect all files") deselect_all_btn.clicked.connect(self._deselect_all_files) row1.addWidget(deselect_all_btn) - sel_core = _make_btn("Core Only", "#555") - sel_core.setFixedWidth(110) + sel_core = _make_icon_btn("β—†", "Core Only: select database files and deselect maps") sel_core.setToolTip("Select only core database files; deselect map files") sel_core.clicked.connect(self._select_core_only) row1.addWidget(sel_core) - import_btn = _make_btn("Import Selected β†’ files/", "#007acc") - import_btn.setFixedWidth(180) + import_btn = _make_icon_btn("πŸ“₯", "Import selected files into files/") import_btn.setEnabled(False) import_btn.setToolTip("Replace files/ with exactly the checked files above") - import_btn.clicked.connect(self._import_files) + import_btn.clicked.connect(lambda _checked=False: self._import_files()) self._register_import_button(import_btn) row1.addWidget(import_btn) @@ -1805,7 +1812,7 @@ class WorkflowTab(QWidget): ) import_btn.setFixedSize(_IMP_W, _IMP_H) import_btn.setEnabled(False) - import_btn.clicked.connect(self._import_files) + import_btn.clicked.connect(lambda _checked=False: self._import_files()) self._register_import_button(import_btn) import_row.addWidget(import_btn) clear_translated_btn = QPushButton("βœ• Clear translated/") @@ -2510,6 +2517,8 @@ class WorkflowTab(QWidget): ) self.file_list.clear() self._set_import_buttons_enabled(False) + self._last_import_signature = None + self._pending_import_signature = None # Reset ACE state from any previous detection self._ace_encrypted = False @@ -2646,7 +2655,7 @@ class WorkflowTab(QWidget): self._log(f"Found {len(items)} importable file(s).") self._populate_preprocess_paths() if items: - self._log("Choose files to import, then click Import Selected β†’ files/.") + self._log("Choose files to import, then click πŸ“₯ to copy them into files/.") def _select_all_files(self): count = self.file_list.count() @@ -2707,22 +2716,50 @@ class WorkflowTab(QWidget): finally: self._syncing_file_checks = False - def _import_files(self): + def _selected_import_items(self) -> list[dict]: selected = [] for i in range(self.file_list.count()): lw = self.file_list.item(i) if lw.checkState() == Qt.Checked: selected.append(lw.data(Qt.UserRole)) + return selected + def _import_signature(self, selected: list[dict] | None = None) -> tuple[str, ...]: + selected = selected if selected is not None else self._selected_import_items() + return tuple(sorted(str(item.get("name", "")) for item in selected if item)) + + def _auto_import_if_needed(self) -> None: + selected = self._selected_import_items() + signature = self._import_signature(selected) + if not signature: + return + if signature in (self._last_import_signature, self._pending_import_signature): + return + self._log("Auto-importing checked project files into files/ before leaving Project.") + self._import_files(confirm=False, selected=selected, signature=signature) + + def _import_files( + self, + confirm: bool = True, + selected: list[dict] | None = None, + signature: tuple[str, ...] | None = None, + ): + selected = selected if selected is not None else self._selected_import_items() if not selected: self._log("⚠ No files selected.") return - if not self._confirm_import_overwrite(selected): + signature = signature if signature is not None else self._import_signature(selected) + if self._pending_import_signature == signature: + self._log("β„Ή Import for the current selection is already running.") + return + + if confirm and not self._confirm_import_overwrite(selected): self._log("β„Ή Import cancelled; files/ was left unchanged.") return self._set_import_buttons_enabled(False) + self._pending_import_signature = signature worker = _ImportWorker(selected, "files") worker.log.connect(self._log) worker.done.connect(self._on_import_done) @@ -2794,6 +2831,9 @@ class WorkflowTab(QWidget): self._log(f"⚠ {len(errors)} error(s) during import:") for e in errors[:10]: self._log(f" {e}") + else: + self._last_import_signature = self._pending_import_signature + self._pending_import_signature = None self._log(f"βœ… Imported {count} file(s) into files/") # ─────────────────────────────────────────────────────────────────────────