""" EU-Utility - Universal Search (Core Framework Component) Built-in quick search - not a plugin. Plugins register searchable content. """ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QListWidget, QListWidgetItem, QFrame ) from PyQt6.QtCore import Qt, QTimer from core.icon_manager import get_icon_manager class UniversalSearchView(QWidget): """Universal search interface - built into the framework. Plugins can register searchable content via PluginAPI: api.register_search_provider( name="My Plugin", search_func=my_search_function ) Press Ctrl+Shift+F to open quick search anywhere. """ def __init__(self, overlay_window, parent=None): super().__init__(parent) self.overlay = overlay_window self.search_providers = [] # Registered search providers self.icon_manager = get_icon_manager() self._setup_ui() def _setup_ui(self): """Create the search UI.""" layout = QVBoxLayout(self) layout.setSpacing(16) layout.setContentsMargins(24, 24, 24, 24) # Header with icon header_layout = QHBoxLayout() header_layout.setSpacing(12) header_icon = QLabel() header_pixmap = self.icon_manager.get_pixmap("search", size=28) header_icon.setPixmap(header_pixmap) header_layout.addWidget(header_icon) header = QLabel("Universal Search") header.setStyleSheet("font-size: 24px; font-weight: bold; color: white;") header_layout.addWidget(header) header_layout.addStretch() layout.addLayout(header_layout) # Search input self.search_input = QLineEdit() self.search_input.setPlaceholderText("Type to search across all plugins...") self.search_input.setStyleSheet(""" QLineEdit { background-color: rgba(20, 31, 35, 0.95); color: white; border: 2px solid rgba(255, 140, 66, 0.5); border-radius: 8px; padding: 15px; font-size: 16px; } QLineEdit:focus { border: 2px solid #ff8c42; } """) self.search_input.textChanged.connect(self._on_search) layout.addWidget(self.search_input) # Results list self.results_list = QListWidget() self.results_list.setStyleSheet(""" QListWidget { background-color: rgba(20, 31, 35, 0.95); border: 1px solid rgba(255, 140, 66, 0.1); border-radius: 8px; color: white; } QListWidget::item { padding: 10px; border-bottom: 1px solid rgba(255, 140, 66, 0.05); } QListWidget::item:selected { background-color: rgba(255, 140, 66, 0.3); } """) self.results_list.itemClicked.connect(self._on_result_clicked) layout.addWidget(self.results_list) # Hint hint = QLabel("Tip: Press Enter to select, Esc to close") hint.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;") layout.addWidget(hint) # Debounce timer self.search_timer = QTimer() self.search_timer.setSingleShot(True) self.search_timer.timeout.connect(self._perform_search) # Focus search input self.search_input.setFocus() def _on_search(self, text: str): """Handle search input change.""" if len(text) < 2: self.results_list.clear() return # Debounce search self.search_timer.stop() self.search_timer.start(300) # 300ms delay def _perform_search(self): """Perform the search.""" query = self.search_input.text().lower() self.results_list.clear() # Search through registered providers for provider in self.search_providers: try: results = provider['search_func'](query) for result in results: item = QListWidgetItem(f"[{provider['name']}] {result['title']}") item.setData(Qt.ItemDataRole.UserRole, result) self.results_list.addItem(item) except Exception as e: print(f"[UniversalSearch] Provider '{provider['name']}' error: {e}") # Add default results if no providers if not self.search_providers: item = QListWidgetItem("Install plugins to enable search functionality") item.setFlags(item.flags() & ~Qt.ItemFlag.ItemIsEnabled) self.results_list.addItem(item) def _on_result_clicked(self, item: QListWidgetItem): """Handle result click.""" result = item.data(Qt.ItemDataRole.UserRole) if result and 'action' in result: result['action']() def register_provider(self, name: str, search_func): """Register a search provider from a plugin. Args: name: Provider name (shown in brackets) search_func: Function that takes query string and returns list of dicts """ self.search_providers.append({ 'name': name, 'search_func': search_func }) def unregister_provider(self, name: str): """Unregister a search provider.""" self.search_providers = [ p for p in self.search_providers if p['name'] != name ] def keyPressEvent(self, event): """Handle key press events.""" if event.key() == Qt.Key.Key_Escape: self.hide() elif event.key() == Qt.Key.Key_Return: # Select first result if self.results_list.count() > 0: item = self.results_list.item(0) self._on_result_clicked(item) else: super().keyPressEvent(event)