""" 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, QApplication from PyQt6.QtGui import QAction 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()