fix(jira-knowledge): replace hover tooltip with click-based popup for help button

Native Qt tooltips on macOS are unreliable (delayed or invisible). Replace
with a QFrame popup that appears on click, positioned below the ? button,
and closes when clicking elsewhere (Qt.Popup flag handles ESC and outside-click).

- Store help text in self._help_text instead of setToolTip()
- Add _toggle_help_popup() method: creates/destroys floating QFrame
- Update test to check _help_text attribute instead of toolTip()

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
thanhnv
2026-09-08 10:08:15 +09:00
co-authored by Claude Opus 5
parent 42f4a058ea
commit 0cb6035863
2 changed files with 55 additions and 17 deletions
+6 -5
View File
@@ -51,15 +51,16 @@ def test_help_icon_exists(dialog):
def test_help_icon_has_tooltip(dialog): def test_help_icon_has_tooltip(dialog):
"""Help icon must have a non-empty tooltip with both Project ID and Jira Key explanations.""" """Help icon must store help text with both Project ID and Jira Key explanations."""
from PySide6.QtWidgets import QToolButton from PySide6.QtWidgets import QToolButton
dlg = dialog dlg = dialog
help_icons = dlg.findChildren(QToolButton) help_icons = dlg.findChildren(QToolButton)
assert help_icons, "No help button found" assert help_icons, "No help button found"
tooltip = help_icons[0].toolTip() # Help text is stored in _help_text for click-based popup
assert tooltip, "Help button has no tooltip" help_text = getattr(dlg, '_help_text', '') or help_icons[0].toolTip()
assert "Project ID" in tooltip, "Tooltip missing Project ID explanation" assert help_text, "Help button has no help content"
assert "Jira Key" in tooltip, "Tooltip missing Jira Key explanation" assert "Project ID" in help_text, "Help content missing Project ID explanation"
assert "Jira Key" in help_text, "Help content missing Jira Key explanation"
# ---- Help text content ---------------------------------------------------- # ---- Help text content ----------------------------------------------------
+49 -12
View File
@@ -11,12 +11,12 @@ setup dialog; OneDrive/SharePoint: neither, they only toggle).
""" """
from __future__ import annotations from __future__ import annotations
from PySide6.QtCore import Qt from PySide6.QtCore import Qt, QPoint
from PySide6.QtGui import QFont from PySide6.QtGui import QFont
from PySide6.QtWidgets import ( from PySide6.QtWidgets import (
QCheckBox, QDialog, QFormLayout, QFrame, QGroupBox, QHBoxLayout, QLabel, QCheckBox, QDialog, QFormLayout, QFrame, QGroupBox, QHBoxLayout, QLabel,
QLineEdit, QMessageBox, QPushButton, QScrollArea, QToolButton, QToolTip, QLineEdit, QMessageBox, QPushButton, QScrollArea, QToolButton,
QVBoxLayout, QWidget, QVBoxLayout, QWidget, QApplication,
) )
from ..core.ext_connectors import CATEGORIES as EXT_CATEGORIES from ..core.ext_connectors import CATEGORIES as EXT_CATEGORIES
@@ -109,12 +109,6 @@ class JiraConnectDialog(QDialog):
mapping_label = QLabel(tr("connectors.jira_kb_mapping_label")) mapping_label = QLabel(tr("connectors.jira_kb_mapping_label"))
self.help_icon = QToolButton() self.help_icon = QToolButton()
self.help_icon.setText("?") self.help_icon.setText("?")
self.help_icon.setToolTip(
"<b>" + tr("connectors.jira_kb_project_id_title") + "</b><br>"
+ tr("connectors.jira_kb_project_id_help")
+ "<br><br><b>" + tr("connectors.jira_kb_jira_key_title") + "</b><br>"
+ tr("connectors.jira_kb_jira_key_help")
)
self.help_icon.setStyleSheet(""" self.help_icon.setStyleSheet("""
QToolButton { QToolButton {
border: 1px solid #5B9BD5; border: 1px solid #5B9BD5;
@@ -135,9 +129,15 @@ class JiraConnectDialog(QDialog):
color: white; color: white;
} }
""") """)
# Native Qt tooltip: hover to show, no click handler needed. # Click-based popover: build help content once, show on click
# AutoRaise=False keeps the button always visible (not faded out). self._help_text = (
self.help_icon.setAutoRaise(False) "<b>" + tr("connectors.jira_kb_project_id_title") + "</b><br>"
+ tr("connectors.jira_kb_project_id_help")
+ "<br><br><b>" + tr("connectors.jira_kb_jira_key_title") + "</b><br>"
+ tr("connectors.jira_kb_jira_key_help")
)
self._help_popup = None
self.help_icon.clicked.connect(self._toggle_help_popup)
label_row = QHBoxLayout() label_row = QHBoxLayout()
label_row.setSpacing(4) label_row.setSpacing(4)
label_row.addWidget(mapping_label) label_row.addWidget(mapping_label)
@@ -188,6 +188,43 @@ class JiraConnectDialog(QDialog):
self.kb_enabled.toggled.connect(self._update_sync_state) self.kb_enabled.toggled.connect(self._update_sync_state)
self._update_sync_state(self.kb_enabled.isChecked()) self._update_sync_state(self.kb_enabled.isChecked())
def _toggle_help_popup(self) -> None:
"""Show/hide a floating help popup when the ? button is clicked."""
if self._help_popup is not None and self._help_popup.isVisible():
self._help_popup.hide()
self._help_popup.deleteLater()
self._help_popup = None
return
popup = QFrame(self)
popup.setWindowFlags(Qt.Popup | Qt.FramelessWindowHint)
popup.setAttribute(Qt.WA_DeleteOnClose, False)
popup.setStyleSheet("""
QFrame {
background: #2D2D2D;
border: 1px solid #5B9BD5;
border-radius: 8px;
padding: 12px;
}
""")
popup_layout = QVBoxLayout(popup)
popup_layout.setContentsMargins(12, 10, 12, 10)
content = QLabel(self._help_text)
content.setWordWrap(True)
content.setMaximumWidth(380)
content.setStyleSheet("color: #E0E0E0; font-size: 12px; background: transparent; border: none;")
content.setTextFormat(Qt.RichText)
content.setOpenExternalLinks(True)
popup_layout.addWidget(content)
# Position below the help button
btn_global = self.help_icon.mapToGlobal(QPoint(0, self.help_icon.height()))
popup.move(btn_global)
popup.show()
popup.raise_()
self._help_popup = popup
def _on_paste(self, text: str) -> None: def _on_paste(self, text: str) -> None:
"""Auto-fill Base URL from a pasted Jira link.""" """Auto-fill Base URL from a pasted Jira link."""
import re import re