feat: Widget Registry - plugins register widgets dynamically

This commit is contained in:
LemonNexus 2026-02-15 16:16:07 +00:00
parent 3ee405475f
commit defb83d213
2 changed files with 154 additions and 81 deletions

View File

@ -1590,55 +1590,55 @@ class OverlayWindow(QMainWindow):
layout.addWidget(header)
# Description
desc = QLabel("Add overlay widgets to your game. Drag to position, resize, and configure.")
desc = QLabel("Add overlay widgets to your game. Install plugins to get more widgets.")
desc.setStyleSheet(f"color: {c['text_secondary']}; font-size: 12px;")
desc.setWordWrap(True)
layout.addWidget(desc)
# Built-in widgets section
builtin_header = QLabel("Built-in Widgets")
builtin_header.setStyleSheet(f"""
color: {c['text_primary']};
font-weight: {EU_TYPOGRAPHY['weight_bold']};
font-size: 14px;
margin-top: 16px;
padding-bottom: 8px;
border-bottom: 1px solid {c['border_default']};
""")
layout.addWidget(builtin_header)
# Get registered widgets
from core.widget_registry import get_widget_registry
registry = get_widget_registry()
widgets = registry.get_all_widgets()
# Clock widget button
clock_btn = self._create_widget_button(
"⏰ Clock",
"A customizable clock with 12/24h format and date display",
lambda: self._add_clock_widget()
)
layout.addWidget(clock_btn)
# System monitor widget button
monitor_btn = self._create_widget_button(
"📊 System Monitor",
"Monitor CPU and RAM usage in real-time",
lambda: self._add_system_monitor_widget()
)
layout.addWidget(monitor_btn)
# Plugin widgets section
plugin_header = QLabel("Plugin Widgets")
plugin_header.setStyleSheet(f"""
color: {c['text_primary']};
font-weight: {EU_TYPOGRAPHY['weight_bold']};
font-size: 14px;
margin-top: 24px;
padding-bottom: 8px;
border-bottom: 1px solid {c['border_default']};
""")
layout.addWidget(plugin_header)
plugin_info = QLabel("Install plugins from the Plugin Store to add more widgets here.")
plugin_info.setStyleSheet(f"color: {c['text_muted']}; font-size: 12px; font-style: italic;")
plugin_info.setWordWrap(True)
layout.addWidget(plugin_info)
if widgets:
# Available widgets section
available_header = QLabel("Available Widgets")
available_header.setStyleSheet(f"""
color: {c['text_primary']};
font-weight: {EU_TYPOGRAPHY['weight_bold']};
font-size: 14px;
margin-top: 16px;
padding-bottom: 8px;
border-bottom: 1px solid {c['border_default']};
""")
layout.addWidget(available_header)
# Create buttons for each registered widget
for widget_info in widgets:
widget_btn = self._create_widget_button(
f"{widget_info.icon} {widget_info.name}",
widget_info.description,
lambda wid=widget_info.id: self._add_registered_widget(wid)
)
layout.addWidget(widget_btn)
else:
# No widgets available
no_widgets = QLabel("No widgets available")
no_widgets.setStyleSheet(f"""
color: {c['text_muted']};
font-size: 14px;
font-style: italic;
margin-top: 24px;
""")
no_widgets.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(no_widgets)
# Install info
install_info = QLabel("Install plugins from the Plugin Store to add widgets here.")
install_info.setStyleSheet(f"color: {c['text_muted']}; font-size: 12px;")
install_info.setWordWrap(True)
install_info.setAlignment(Qt.AlignmentFlag.AlignCenter)
layout.addWidget(install_info)
layout.addStretch()
@ -1732,13 +1732,17 @@ class OverlayWindow(QMainWindow):
return tab
def _add_clock_widget(self):
"""Add a clock widget to the overlay."""
def _add_registered_widget(self, widget_id: str):
"""Add a registered widget to the overlay."""
try:
from core.widget_system import ClockWidget
from core.widget_registry import get_widget_registry
registry = get_widget_registry()
# Create widget with self as parent
widget = ClockWidget(parent=self)
# Create widget using registry
widget = registry.create_widget(widget_id)
if not widget:
print(f"[Overlay] Failed to create widget: {widget_id}")
return
# Position near center of screen
screen = QApplication.primaryScreen().geometry()
@ -1756,38 +1760,8 @@ class OverlayWindow(QMainWindow):
self._active_widgets = []
self._active_widgets.append(widget)
print(f"[Overlay] Clock widget added at ({x}, {y})")
print(f"[Overlay] Widget {widget_id} added at ({x}, {y})")
except Exception as e:
print(f"[Overlay] Error adding clock widget: {e}")
import traceback
traceback.print_exc()
def _add_system_monitor_widget(self):
"""Add a system monitor widget to the overlay."""
try:
from core.widget_system import SystemMonitorWidget
# Create widget with self as parent
widget = SystemMonitorWidget(parent=self)
# Position below the clock
screen = QApplication.primaryScreen().geometry()
x = (screen.width() - 200) // 2
y = (screen.height() - 100) // 2 + 120
widget.move(x, y)
# Show and raise
widget.show()
widget.raise_()
widget.activateWindow()
# Store reference to prevent garbage collection
if not hasattr(self, '_active_widgets'):
self._active_widgets = []
self._active_widgets.append(widget)
print(f"[Overlay] System monitor widget added at ({x}, {y})")
except Exception as e:
print(f"[Overlay] Error adding system monitor widget: {e}")
print(f"[Overlay] Error adding widget {widget_id}: {e}")
import traceback
traceback.print_exc()

99
core/widget_registry.py Normal file
View File

@ -0,0 +1,99 @@
"""
EU-Utility - Widget Registry
Allows plugins to register widgets that appear in the Widgets tab.
"""
from typing import Dict, List, Callable, Optional
from dataclasses import dataclass
from PyQt6.QtWidgets import QWidget
@dataclass
class WidgetInfo:
"""Information about a registered widget."""
id: str
name: str
description: str
icon: str # emoji or icon name
creator: Callable[[], QWidget] # Function that creates the widget
plugin_id: str # Which plugin registered this
class WidgetRegistry:
"""Registry for plugin-provided widgets."""
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._widgets: Dict[str, WidgetInfo] = {}
return cls._instance
def register_widget(
self,
widget_id: str,
name: str,
description: str,
icon: str,
creator: Callable[[], QWidget],
plugin_id: str = "unknown"
):
"""Register a widget.
Args:
widget_id: Unique identifier for this widget type
name: Display name
description: Short description shown in UI
icon: Emoji or icon name
creator: Function that returns a QWidget instance
plugin_id: ID of the plugin registering this widget
"""
self._widgets[widget_id] = WidgetInfo(
id=widget_id,
name=name,
description=description,
icon=icon,
creator=creator,
plugin_id=plugin_id
)
print(f"[WidgetRegistry] Registered widget: {name} (from {plugin_id})")
def unregister_widget(self, widget_id: str):
"""Unregister a widget."""
if widget_id in self._widgets:
del self._widgets[widget_id]
print(f"[WidgetRegistry] Unregistered widget: {widget_id}")
def get_widget(self, widget_id: str) -> Optional[WidgetInfo]:
"""Get widget info by ID."""
return self._widgets.get(widget_id)
def get_all_widgets(self) -> List[WidgetInfo]:
"""Get all registered widgets."""
return list(self._widgets.values())
def get_widgets_by_plugin(self, plugin_id: str) -> List[WidgetInfo]:
"""Get all widgets registered by a specific plugin."""
return [w for w in self._widgets.values() if w.plugin_id == plugin_id]
def create_widget(self, widget_id: str) -> Optional[QWidget]:
"""Create an instance of a registered widget."""
info = self._widgets.get(widget_id)
if info:
try:
return info.creator()
except Exception as e:
print(f"[WidgetRegistry] Error creating widget {widget_id}: {e}")
return None
def clear(self):
"""Clear all registered widgets."""
self._widgets.clear()
# Global registry instance
def get_widget_registry() -> WidgetRegistry:
"""Get the global widget registry."""
return WidgetRegistry()