From c4afc31e34725a2c792bced065db2482f9fee2d2 Mon Sep 17 00:00:00 2001 From: snoopy Date: Fri, 4 Sep 2026 12:17:34 +0200 Subject: [PATCH] Renommage des onglets Double-clic sur un onglet pour le renommer, ou clic droit pour le menu. Le menu contextuel permet aussi de fermer l'onglet vise ou tous les autres. Le nom saisi est conserve tel quel : un onglet renomme "Serveur prod" ne retrouve pas son libelle d'origine, y compris apres une reconnexion. Un nom vide ou uniquement compose d'espaces est refuse, et annuler la saisie laisse le nom inchange. La fermeture des autres onglets parcourt la liste a l'envers : retirer un onglet decale l'index des suivants, un parcours en avant en sauterait. Co-Authored-By: Claude Opus 5 --- README.md | 2 ++ app/main_window.py | 41 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index de0deb6..c36220c 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Client SSH/SFTP personnel (type MobaXterm/Termius/Bitvise), 100% local, sans com - Confort d'affichage : zoom `Ctrl+molette` ou `Ctrl+ +/-` (`Ctrl+0` remet a zero, la taille est memorisee), barre de defilement de l'historique, `Maj+PagePrec/Suiv`, `Ctrl+Fin` pour revenir en direct, et recherche dans l'historique avec `Ctrl+F` +- Onglets renommables (double-clic sur l'onglet, ou clic droit) et deplaçables ; + le clic droit permet aussi de fermer les autres onglets - Panneau des sessions redimensionnable en attrapant le separateur, et masquable entierement (`F9`, menu Affichage, ou la croix du panneau) pour donner tout l'ecran au terminal - **Tunnels SSH** (redirection de ports) : bouton "Tunnels" dans la barre d'un onglet SSH. diff --git a/app/main_window.py b/app/main_window.py index 12f465a..ae0d0fa 100644 --- a/app/main_window.py +++ b/app/main_window.py @@ -12,7 +12,7 @@ from PySide6.QtGui import QAction, QActionGroup, QKeySequence, QShortcut from PySide6.QtWidgets import ( QMainWindow, QDockWidget, QTreeWidget, QTreeWidgetItem, QTabWidget, QWidget, QVBoxLayout, QHBoxLayout, QToolButton, QLabel, QMenu, QMessageBox, QSplitter, - QStatusBar, QFrame, QApplication, QLineEdit, QFileDialog, + QStatusBar, QFrame, QApplication, QLineEdit, QFileDialog, QInputDialog, ) from app.session_manager import SessionStore, Session, AUTH_KEY, AUTH_PASSWORD @@ -661,6 +661,10 @@ class MainWindow(QMainWindow): self.tabs = QTabWidget() self.tabs.setTabsClosable(True) self.tabs.setMovable(True) + # double-clic sur un onglet pour le renommer, clic droit pour le menu + self.tabs.tabBarDoubleClicked.connect(self.rename_tab) + self.tabs.tabBar().setContextMenuPolicy(Qt.CustomContextMenu) + self.tabs.tabBar().customContextMenuRequested.connect(self._on_tab_context_menu) self.tabs.tabCloseRequested.connect(self._on_tab_close) self.setCentralWidget(self.tabs) @@ -896,6 +900,41 @@ class MainWindow(QMainWindow): def _register_broadcast(self, tab) -> None: tab.terminal.data_to_send.connect(lambda data, t=tab: self._broadcast(data, t)) + # ---------- renommage des onglets ---------- + def rename_tab(self, index: int) -> None: + if index < 0: + return + actuel = self.tabs.tabText(index) + nouveau, ok = QInputDialog.getText( + self, "Renommer l'onglet", "Nom de l'onglet :", text=actuel + ) + if ok and nouveau.strip(): + self.tabs.setTabText(index, nouveau.strip()) + + def _on_tab_context_menu(self, pos) -> None: + bar = self.tabs.tabBar() + index = bar.tabAt(pos) + if index < 0: + return + menu = QMenu(self) + act_rename = menu.addAction("Renommer... (double-clic)") + menu.addSeparator() + act_close = menu.addAction("Fermer cet onglet") + act_close_others = menu.addAction("Fermer les autres onglets") + act_close_others.setEnabled(self.tabs.count() > 1) + + action = menu.exec(bar.mapToGlobal(pos)) + if action == act_rename: + self.rename_tab(index) + elif action == act_close: + self._on_tab_close(index) + elif action == act_close_others: + # on ferme par la fin : retirer un onglet decale les suivants + cible = self.tabs.widget(index) + for other in range(self.tabs.count() - 1, -1, -1): + if self.tabs.widget(other) is not cible: + self._on_tab_close(other) + def _on_tab_close(self, index: int) -> None: widget = self.tabs.widget(index) if isinstance(widget, (BaseTerminalTab, SFTPTabWidget)):