From 3f48ac5fb07c8763bf1d0c1a7fc09f4f2255bb45 Mon Sep 17 00:00:00 2001 From: arch_agent Date: Sun, 19 Jul 2026 16:12:50 +0200 Subject: [PATCH] =?UTF-8?q?v0.3:=20Enhanced=20Script=20Mods=20tab=20?= =?UTF-8?q?=E2=80=94=20grouped=20by=20mod=20name,=20file=20counts,=20toggl?= =?UTF-8?q?e,=20context=20menu?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Script mods now grouped by mod name instead of flat file list - Shows file count per mod and category totals - Toggle button to enable/disable script mods - Right-click context menu for script mods - Info label showing total mods and files - Support for redscript, TweakXL, CET, red4ext, ArchiveXL --- mod_manager.py | 83 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 77 insertions(+), 6 deletions(-) diff --git a/mod_manager.py b/mod_manager.py index 1196d4e..b69720c 100644 --- a/mod_manager.py +++ b/mod_manager.py @@ -856,11 +856,24 @@ class ModManagerWindow(QMainWindow): layout.addWidget(QLabel("📜 Script-based mods (redscript, TweakXL, CET, red4ext, ArchiveXL):")) self.script_tree = QTreeWidget() - self.script_tree.setHeaderLabels(["Type", "Path", "Status"]) + self.script_tree.setHeaderLabels(["Type", "Mod Name", "Path", "Files", "Status"]) self.script_tree.setAlternatingRowColors(True) + header = self.script_tree.header() + header.setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents) + header.setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch) + header.setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents) + header.setSectionResizeMode(4, QHeaderView.ResizeMode.ResizeToContents) + self.script_tree.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu) + self.script_tree.customContextMenuRequested.connect(self._script_context_menu) layout.addWidget(self.script_tree, 1) - # Add script mod button + # Info label + self.script_info_label = QLabel("Ready. Click Refresh to scan.") + self.script_info_label.setStyleSheet("color: #888; font-size: 11px;") + layout.addWidget(self.script_info_label) + + # Buttons btn_layout = QHBoxLayout() add_script_btn = QPushButton("➕ Add Script Mod (.yaml, .reds, .zip, .7z)") add_script_btn.clicked.connect(self.add_script_mod) @@ -870,6 +883,10 @@ class ModManagerWindow(QMainWindow): refresh_script_btn.clicked.connect(self.refresh_script_mods) btn_layout.addWidget(refresh_script_btn) + toggle_script_btn = QPushButton("🔄 Toggle Selected") + toggle_script_btn.clicked.connect(self._toggle_script_mod) + btn_layout.addWidget(toggle_script_btn) + btn_layout.addStretch() layout.addLayout(btn_layout) @@ -1113,23 +1130,77 @@ class ModManagerWindow(QMainWindow): "archivexl": "🗂️", } + total_mods = 0 + total_files = 0 + for category, files in self.script_mods.items(): if not files: continue + # Group by mod name (top-level folder or filename) + mod_groups = {} + for f in files: + # Extract mod name from path + path_parts = f.split("/") + if category == "cet" and len(path_parts) >= 7: + # bin/x64/plugins/cyber_engine_tweaks/mods//... + mod_name = path_parts[6] + elif category == "red4ext" and len(path_parts) >= 3: + # red4ext/plugins//... + mod_name = path_parts[2] + elif category == "redscript" and len(path_parts) >= 3: + # r6/scripts//... + mod_name = path_parts[2] if path_parts[2] != "r6" else path_parts[-1] + elif category == "tweakxl" and len(path_parts) >= 3: + # r6/tweaks//... + mod_name = path_parts[2] if path_parts[2] != "r6" else path_parts[-1] + else: + mod_name = path_parts[-1] + + if mod_name not in mod_groups: + mod_groups[mod_name] = [] + mod_groups[mod_name].append(f) + parent = QTreeWidgetItem() parent.setText(0, f"{icons.get(category, '📋')} {category.upper()}") - parent.setText(2, f"{len(files)} items") + parent.setText(1, f"{len(mod_groups)} mods") + parent.setText(3, f"{len(files)} files") + parent.setText(4, "✅ Active") parent.setForeground(0, QColor("#00ff9f")) + parent.setForeground(4, QColor("#00ff9f")) + parent.setExpanded(True) - for f in files: + for mod_name, mod_files in sorted(mod_groups.items()): child = QTreeWidgetItem() - child.setText(1, f) - child.setText(2, "✅ Active") + child.setText(1, mod_name) + child.setText(3, str(len(mod_files))) + + # Check if disabled (.disabled suffix on folder or file) + is_disabled = any(".disabled" in f for f in mod_files) + child.setText(4, "❌ Disabled" if is_disabled else "✅ Active") + if is_disabled: + child.setForeground(4, QColor("#ff0080")) + else: + child.setForeground(4, QColor("#00ff9f")) + + child.setData(0, Qt.ItemDataRole.UserRole, { + "category": category, + "mod_name": mod_name, + "files": mod_files, + "disabled": is_disabled + }) + + # Show first file as path hint + child.setText(2, mod_files[0] if mod_files else "") + parent.addChild(child) + total_mods += 1 + total_files += len(mod_files) self.script_tree.addTopLevelItem(parent) parent.setExpanded(True) + + self.script_info_label.setText(f"Total: {total_mods} mods, {total_files} files") def _on_mod_checkbox_changed(self, item, column): if column != 0: