""" EU-Utility - System Tray Icon ============================= System tray implementation with right-click menu. Replaces the floating icon with a proper tray icon. """ from PyQt6.QtWidgets import QSystemTrayIcon, QMenu, QAction, QApplication from PyQt6.QtCore import QTimer, pyqtSignal, QObject from PyQt6.QtGui import QIcon, QColor, QPainter, QFont, QFontMetrics from core.logger import get_logger logger = get_logger(__name__) class TrayIcon(QObject): """ System tray icon for EU-Utility. Features: - Right-click context menu - Show/hide dashboard - Toggle activity bar - Settings access - Quit option """ show_dashboard = pyqtSignal() toggle_activity_bar = pyqtSignal() open_settings = pyqtSignal() quit_app = pyqtSignal() def __init__(self, app: QApplication, parent=None): super().__init__(parent) self.app = app self.tray_icon = None self.menu = None self._create_icon() self._setup_menu() self._setup_visibility_timer() def _create_icon(self): """Create the tray icon with EU logo.""" # Create a simple colored circle icon with "EU" text pixmap = QIcon.fromTheme("applications-system") # If no system icon, create custom if pixmap.isNull(): from PyQt6.QtGui import QPixmap, QPainter, QColor, QFont px = QPixmap(64, 64) px.fill(QColor(255, 140, 66)) # Orange background painter = QPainter(px) painter.setPen(QColor(255, 255, 255)) font = QFont("Segoe UI", 24, QFont.Weight.Bold) painter.setFont(font) painter.drawText(px.rect(), Qt.AlignmentFlag.AlignCenter, "EU") painter.end() pixmap = QIcon(px) self.tray_icon = QSystemTrayIcon(self) self.tray_icon.setIcon(pixmap) self.tray_icon.setToolTip("EU-Utility") # Show the icon self.tray_icon.show() # Connect activation (double-click) self.tray_icon.activated.connect(self._on_activated) def _setup_menu(self): """Setup the right-click context menu.""" self.menu = QMenu() self.menu.setStyleSheet(""" QMenu { background: rgba(35, 35, 35, 0.98); color: white; border: 1px solid rgba(255, 255, 255, 0.1); border-radius: 8px; padding: 8px; } QMenu::item { padding: 10px 24px; border-radius: 6px; } QMenu::item:selected { background: rgba(255, 140, 66, 0.3); } QMenu::separator { height: 1px; background: rgba(255, 255, 255, 0.1); margin: 8px 16px; } """) # Dashboard action self.dashboard_action = QAction("📊 Dashboard", self) self.dashboard_action.triggered.connect(self.show_dashboard) self.menu.addAction(self.dashboard_action) # Activity Bar toggle self.activity_bar_action = QAction("🎮 Activity Bar", self) self.activity_bar_action.setCheckable(True) self.activity_bar_action.triggered.connect(self.toggle_activity_bar) self.menu.addAction(self.activity_bar_action) self.menu.addSeparator() # Settings self.settings_action = QAction("⚙️ Settings", self) self.settings_action.triggered.connect(self.open_settings) self.menu.addAction(self.settings_action) self.menu.addSeparator() # Quit self.quit_action = QAction("❌ Quit", self) self.quit_action.triggered.connect(self.quit_app) self.menu.addAction(self.quit_action) # Set menu self.tray_icon.setContextMenu(self.menu) def _setup_visibility_timer(self): """Setup timer to update menu state.""" self.update_timer = QTimer(self) self.update_timer.timeout.connect(self._update_menu_state) self.update_timer.start(1000) # Update every second def _update_menu_state(self): """Update menu checkbox states.""" # This will be connected to actual state pass def _on_activated(self, reason): """Handle tray icon activation.""" if reason == QSystemTrayIcon.ActivationReason.DoubleClick: self.show_dashboard.emit() def show_notification(self, title: str, message: str, duration: int = 3000): """Show a system notification.""" if self.tray_icon and self.tray_icon.supportsMessages(): self.tray_icon.showMessage( title, message, QSystemTrayIcon.MessageIcon.Information, duration ) def set_activity_bar_checked(self, checked: bool): """Update activity bar menu item state.""" self.activity_bar_action.setChecked(checked) def is_visible(self) -> bool: """Check if tray icon is visible.""" return self.tray_icon and self.tray_icon.isVisible()