feat: FINAL SWARM PART 2 - EU Styling + Dashboard

MAJOR UPDATES:
- Overlay Window completely redesigned with EU styling
- Uses exact EU color palette from game screenshots
- Dark blue/gray backgrounds matching EU UI
- Orange (#ff8c42) accent colors
- Proper EU border styling
- Shadow effects matching game windows

NEW PLUGIN:
- Dashboard - Main overview with stats, quick actions, activity feed
- Shows PED balance, skill count, items, DPP
- Quick action grid for common tasks
- Recent activity timeline
- Pro tips section

EU STYLING APPLIED:
- Header with orange EU-Utility logo
- Plugin bar with EU-styled buttons
- Proper border radius (8px main, 4px buttons)
- Tray menu styled to match
- Consistent color scheme throughout

Total plugins: 19

SWARM COMPLETE! 🚀🚀🚀
This commit is contained in:
LemonNexus 2026-02-13 15:00:48 +00:00
parent 0228a641ed
commit 2abbea9563
3 changed files with 356 additions and 149 deletions

View File

@ -1,7 +1,7 @@
""" """
EU-Utility - Overlay Window EU-Utility - Overlay Window
Spotlight-style overlay with frosted glass effect and Phosphor icons. Fully EU-styled overlay window matching Entropia Universe aesthetic.
""" """
import sys import sys
@ -22,6 +22,8 @@ except ImportError:
PYQT6_AVAILABLE = False PYQT6_AVAILABLE = False
print("PyQt6 not available. Install with: pip install PyQt6") print("PyQt6 not available. Install with: pip install PyQt6")
from core.eu_styles import EU_COLORS, EU_STYLES, get_eu_style
class IconHelper: class IconHelper:
"""Helper to load and render SVG icons.""" """Helper to load and render SVG icons."""
@ -29,7 +31,7 @@ class IconHelper:
ICONS_DIR = Path(__file__).parent.parent / "assets" / "icons" ICONS_DIR = Path(__file__).parent.parent / "assets" / "icons"
@classmethod @classmethod
def get_icon(cls, name, size=24, color="white"): def get_icon(cls, name, size=24):
"""Get QIcon from SVG.""" """Get QIcon from SVG."""
svg_path = cls.ICONS_DIR / f"{name}.svg" svg_path = cls.ICONS_DIR / f"{name}.svg"
if not svg_path.exists(): if not svg_path.exists():
@ -44,50 +46,35 @@ class IconHelper:
painter.end() painter.end()
return QIcon(pixmap) return QIcon(pixmap)
@classmethod
def get_pixmap(cls, name, size=24):
"""Get QPixmap from SVG."""
svg_path = cls.ICONS_DIR / f"{name}.svg"
if not svg_path.exists():
return None
renderer = QSvgRenderer(str(svg_path))
pixmap = QPixmap(size, size)
pixmap.fill(Qt.GlobalColor.transparent)
painter = QPainter(pixmap)
renderer.render(painter)
painter.end()
return pixmap
class OverlayWindow(QMainWindow): class OverlayWindow(QMainWindow):
"""Spotlight-style overlay window.""" """EU-styled overlay window."""
visibility_changed = pyqtSignal(bool) visibility_changed = pyqtSignal(bool)
# Plugin icon mapping - Phosphor solid icons # Plugin icon mapping with EU accent colors
PLUGIN_ICONS = { PLUGIN_ICONS = {
"Universal Search": ("search", "#4a9eff"), "Universal Search": ("search", "#ff8c42"),
"Calculator": ("calculator", "#9c27b0"), "Calculator": ("calculator", "#ff8c42"),
"Spotify": ("music", "#1db954"), "Spotify": ("music", "#1db954"),
"Nexus Search": ("globe", "#ff9800"), "Nexus Search": ("globe", "#ff8c42"),
"Game Reader": ("camera", "#f44336"), "Game Reader": ("camera", "#ff8c42"),
"Skill Scanner": ("skills", "#00bcd4"), "Skill Scanner": ("skills", "#ff8c42"),
} "Loot Tracker": ("loot", "#ff8c42"),
"Mining Helper": ("mob", "#ff8c42"),
# Emoji fallbacks "Chat Logger": ("file", "#ff8c42"),
ICONS = { "Mission Tracker": ("target", "#ff8c42"),
'search': '🔍', "Codex Tracker": ("mob", "#ff8c42"),
'calculator': '🧮', "Auction Tracker": ("ped", "#ff8c42"),
'music': '🎵', "DPP Calculator": ("weapon", "#ff8c42"),
'globe': '🌐', "Enhancer Calc": ("armor", "#ff8c42"),
'skills': '📊', "TP Runner": ("globe", "#ff8c42"),
'camera': '📷', "Inventory": ("loot", "#ff8c42"),
'close': '', "Settings": ("settings", "#ff8c42"),
'menu': '', "Plugin Store": ("external", "#ff8c42"),
"Crafting Calc": ("armor", "#ff8c42"),
"Global Tracker": ("ped", "#ff8c42"),
} }
def __init__(self, plugin_manager=None): def __init__(self, plugin_manager=None):
@ -104,115 +91,108 @@ class OverlayWindow(QMainWindow):
self._setup_ui() self._setup_ui()
self._setup_tray() self._setup_tray()
# Start hidden
self.hide_overlay() self.hide_overlay()
def _setup_window(self): def _setup_window(self):
"""Configure window properties.""" """Configure window with EU styling."""
self.setWindowTitle("EU-Utility") self.setWindowTitle("EU-Utility")
# Frameless, transparent, always on top
self.setWindowFlags( self.setWindowFlags(
Qt.WindowType.FramelessWindowHint | Qt.WindowType.FramelessWindowHint |
Qt.WindowType.WindowStaysOnTopHint | Qt.WindowType.WindowStaysOnTopHint |
Qt.WindowType.Tool Qt.WindowType.Tool
) )
# Transparent background
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
# Size and position - wider but shorter for spotlight style # EU-sized window
self.resize(700, 500) self.resize(800, 550)
self._center_window() self._center_window()
def _center_window(self): def _center_window(self):
"""Center window on screen.""" """Center window on screen."""
screen = QApplication.primaryScreen().geometry() screen = QApplication.primaryScreen().geometry()
x = (screen.width() - self.width()) // 2 x = (screen.width() - self.width()) // 2
y = (screen.height() - self.height()) // 3 # Upper third like Spotlight y = (screen.height() - self.height()) // 3
self.move(x, y) self.move(x, y)
def _setup_ui(self): def _setup_ui(self):
"""Setup the Spotlight-style UI.""" """Setup EU-styled UI."""
# Central widget
central = QWidget() central = QWidget()
self.setCentralWidget(central) self.setCentralWidget(central)
# Main layout
layout = QVBoxLayout(central) layout = QVBoxLayout(central)
layout.setContentsMargins(20, 20, 20, 20) layout.setContentsMargins(15, 15, 15, 15)
layout.setSpacing(0) layout.setSpacing(0)
# Frosted glass container # Main container with EU styling
self.container = QFrame() self.container = QFrame()
self.container.setObjectName("spotlightContainer") self.container.setObjectName("euContainer")
self.container.setStyleSheet(""" self.container.setStyleSheet(f"""
#spotlightContainer { #euContainer {{
background-color: rgba(40, 40, 40, 180); background-color: {EU_COLORS['bg_dark']};
border-radius: 20px; border: 1px solid {EU_COLORS['border_medium']};
border: 1px solid rgba(255, 255, 255, 30); border-radius: 8px;
} }}
""") """)
# Add shadow effect # Add shadow
shadow = QGraphicsDropShadowEffect() shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(30) shadow.setBlurRadius(25)
shadow.setColor(QColor(0, 0, 0, 100)) shadow.setColor(QColor(0, 0, 0, 120))
shadow.setOffset(0, 10) shadow.setOffset(0, 5)
self.container.setGraphicsEffect(shadow) self.container.setGraphicsEffect(shadow)
container_layout = QVBoxLayout(self.container) container_layout = QVBoxLayout(self.container)
container_layout.setContentsMargins(0, 0, 0, 0) container_layout.setContentsMargins(0, 0, 0, 0)
container_layout.setSpacing(0) container_layout.setSpacing(0)
# Header with search bar style # EU-styled header
header = QWidget() header = QWidget()
header.setStyleSheet(""" header.setStyleSheet(f"""
QWidget { QWidget {{
background-color: rgba(255, 255, 255, 15); background-color: {EU_COLORS['bg_header']};
border-top-left-radius: 20px; border-top-left-radius: 8px;
border-top-right-radius: 20px; border-top-right-radius: 8px;
border-bottom: 1px solid rgba(255, 255, 255, 20); border-bottom: 1px solid {EU_COLORS['border_medium']};
} }}
""") """)
header_layout = QHBoxLayout(header) header_layout = QHBoxLayout(header)
header_layout.setContentsMargins(20, 15, 20, 15) header_layout.setContentsMargins(15, 12, 15, 12)
header_layout.setSpacing(15) header_layout.setSpacing(12)
# Search icon # Logo/Icon
search_icon = QLabel("🔍") logo = QLabel("")
search_icon.setStyleSheet("font-size: 18px; background: transparent; border: none;") logo.setStyleSheet("font-size: 18px; background: transparent;")
header_layout.addWidget(search_icon) header_layout.addWidget(logo)
# Title/Search label # Title
title = QLabel("EU-Utility") title = QLabel("EU-Utility")
title.setStyleSheet(""" title.setStyleSheet(f"""
color: rgba(255, 255, 255, 200); color: {EU_COLORS['accent_orange']};
font-size: 16px; font-size: 16px;
font-weight: 500; font-weight: bold;
background: transparent; background: transparent;
border: none;
""") """)
header_layout.addWidget(title) header_layout.addWidget(title)
header_layout.addStretch() header_layout.addStretch()
# Close button (X) - subtle # Close button (EU style)
close_btn = QPushButton("×") close_btn = QPushButton("")
close_btn.setFixedSize(28, 28) close_btn.setFixedSize(28, 28)
close_btn.setStyleSheet(""" close_btn.setStyleSheet(f"""
QPushButton { QPushButton {{
background-color: transparent; background-color: transparent;
color: rgba(255, 255, 255, 150); color: {EU_COLORS['text_muted']};
font-size: 20px; font-size: 16px;
font-weight: 300;
border: none; border: none;
border-radius: 14px; border-radius: 4px;
} }}
QPushButton:hover { QPushButton:hover {{
background-color: rgba(255, 255, 255, 20); background-color: rgba(244, 67, 54, 150);
color: white; color: white;
} }}
""") """)
close_btn.clicked.connect(self.hide_overlay) close_btn.clicked.connect(self.hide_overlay)
header_layout.addWidget(close_btn) header_layout.addWidget(close_btn)
@ -223,8 +203,8 @@ class OverlayWindow(QMainWindow):
self.content_area = QWidget() self.content_area = QWidget()
self.content_area.setStyleSheet("background: transparent;") self.content_area.setStyleSheet("background: transparent;")
content_layout = QVBoxLayout(self.content_area) content_layout = QVBoxLayout(self.content_area)
content_layout.setContentsMargins(20, 20, 20, 20) content_layout.setContentsMargins(15, 15, 15, 15)
content_layout.setSpacing(15) content_layout.setSpacing(12)
# Plugin stack # Plugin stack
self.plugin_stack = QStackedWidget() self.plugin_stack = QStackedWidget()
@ -233,68 +213,65 @@ class OverlayWindow(QMainWindow):
container_layout.addWidget(self.content_area, 1) container_layout.addWidget(self.content_area, 1)
# Plugin selector bar (at bottom) # EU-styled plugin bar
if self.plugin_manager: if self.plugin_manager:
self._setup_plugin_bar(container_layout) self._setup_plugin_bar(container_layout)
layout.addWidget(self.container) layout.addWidget(self.container)
def _setup_plugin_bar(self, layout): def _setup_plugin_bar(self, layout):
"""Setup circular plugin icon bar at bottom with Phosphor icons.""" """Setup EU-styled plugin icon bar."""
bar = QWidget() bar = QWidget()
bar.setStyleSheet(""" bar.setStyleSheet(f"""
QWidget { QWidget {{
background-color: rgba(0, 0, 0, 40); background-color: {EU_COLORS['bg_header']};
border-bottom-left-radius: 20px; border-bottom-left-radius: 8px;
border-bottom-right-radius: 20px; border-bottom-right-radius: 8px;
} border-top: 1px solid {EU_COLORS['border_medium']};
}}
""") """)
bar_layout = QHBoxLayout(bar) bar_layout = QHBoxLayout(bar)
bar_layout.setContentsMargins(20, 12, 20, 12) bar_layout.setContentsMargins(15, 10, 15, 10)
bar_layout.setSpacing(12) bar_layout.setSpacing(8)
bar_layout.addStretch() bar_layout.addStretch()
# Add plugin icons # Add plugin icons
for idx, (plugin_id, plugin) in enumerate(self.plugin_manager.get_all_plugins().items()): for idx, (plugin_id, plugin) in enumerate(self.plugin_manager.get_all_plugins().items()):
btn = QPushButton() btn = QPushButton()
# Get icon and color for this plugin
icon_name, accent_color = self.PLUGIN_ICONS.get( icon_name, accent_color = self.PLUGIN_ICONS.get(
plugin.name, plugin.name,
("target", "#4a9eff") ("target", EU_COLORS['accent_orange'])
) )
# Try to load SVG icon # Load SVG icon
icon = IconHelper.get_icon(icon_name, size=20) icon = IconHelper.get_icon(icon_name, size=18)
if icon: if icon:
btn.setIcon(icon) btn.setIcon(icon)
btn.setIconSize(QSize(20, 20)) btn.setIconSize(QSize(18, 18))
btn.setText("") # No text, just icon
else: else:
# Fallback to emoji btn.setText("")
emoji = self.ICONS.get(icon_name, '')
btn.setText(emoji)
btn.setFixedSize(40, 40) btn.setFixedSize(36, 36)
btn.setStyleSheet(f""" btn.setStyleSheet(f"""
QPushButton {{ QPushButton {{
background-color: rgba(255, 255, 255, 12); background-color: {EU_COLORS['bg_panel']};
border: 1px solid rgba(255, 255, 255, 20); border: 1px solid {EU_COLORS['border_subtle']};
border-radius: 20px; border-radius: 4px;
}} }}
QPushButton:hover {{ QPushButton:hover {{
background-color: rgba(255, 255, 255, 25); background-color: {EU_COLORS['bg_hover']};
border: 1px solid rgba(255, 255, 255, 40); border: 1px solid {EU_COLORS['border_orange']};
}} }}
QPushButton:checked {{ QPushButton:checked {{
background-color: {accent_color}; background-color: {accent_color};
border: 1px solid rgba(255, 255, 255, 50); border: 1px solid {accent_color};
}} }}
""") """)
btn.setCheckable(True) btn.setCheckable(True)
btn.setToolTip(plugin.name) btn.setToolTip(plugin.name)
# Add plugin UI to stack # Add plugin UI
try: try:
plugin_ui = plugin.get_ui() plugin_ui = plugin.get_ui()
if plugin_ui: if plugin_ui:
@ -311,58 +288,51 @@ class OverlayWindow(QMainWindow):
bar_layout.addStretch() bar_layout.addStretch()
layout.addWidget(bar) layout.addWidget(bar)
# Select first plugin by default # Select first
if self.plugin_buttons: if self.plugin_buttons:
self.plugin_buttons[0].setChecked(True) self.plugin_buttons[0].setChecked(True)
def _switch_plugin(self, index, button): def _switch_plugin(self, index, button):
"""Switch to selected plugin.""" """Switch to selected plugin."""
# Uncheck all other buttons
for btn in self.plugin_buttons: for btn in self.plugin_buttons:
if btn != button: if btn != button:
btn.setChecked(False) btn.setChecked(False)
# Ensure clicked button stays checked
button.setChecked(True) button.setChecked(True)
# Switch stack
self.plugin_stack.setCurrentIndex(index) self.plugin_stack.setCurrentIndex(index)
def _setup_tray(self): def _setup_tray(self):
"""Setup system tray icon.""" """Setup system tray."""
self.tray_icon = QSystemTrayIcon(self) self.tray_icon = QSystemTrayIcon(self)
# Use default icon if custom not found
icon_path = Path("assets/icon.ico") icon_path = Path("assets/icon.ico")
if icon_path.exists(): if icon_path.exists():
self.tray_icon.setIcon(QIcon(str(icon_path))) self.tray_icon.setIcon(QIcon(str(icon_path)))
# Tray menu
tray_menu = QMenu() tray_menu = QMenu()
tray_menu.setStyleSheet(""" tray_menu.setStyleSheet(f"""
QMenu { QMenu {{
background-color: rgba(40, 40, 40, 240); background-color: {EU_COLORS['bg_dark']};
color: white; color: white;
border: 1px solid rgba(255, 255, 255, 30); border: 1px solid {EU_COLORS['border_medium']};
border-radius: 8px; border-radius: 6px;
padding: 8px; padding: 8px;
} }}
QMenu::item { QMenu::item {{
padding: 8px 20px; padding: 8px 20px;
border-radius: 4px; border-radius: 4px;
} }}
QMenu::item:selected { QMenu::item:selected {{
background-color: #4a9eff; background-color: {EU_COLORS['accent_orange']};
} }}
""") """)
show_action = QAction("🔍 Show EU-Utility", self) show_action = QAction(" Show EU-Utility", self)
show_action.triggered.connect(self.show_overlay) show_action.triggered.connect(self.show_overlay)
tray_menu.addAction(show_action) tray_menu.addAction(show_action)
tray_menu.addSeparator() tray_menu.addSeparator()
quit_action = QAction(" Quit", self) quit_action = QAction(" Quit", self)
quit_action.triggered.connect(self.quit_app) quit_action.triggered.connect(self.quit_app)
tray_menu.addAction(quit_action) tray_menu.addAction(quit_action)
@ -371,12 +341,12 @@ class OverlayWindow(QMainWindow):
self.tray_icon.show() self.tray_icon.show()
def _tray_activated(self, reason): def _tray_activated(self, reason):
"""Handle tray icon activation.""" """Handle tray activation."""
if reason == QSystemTrayIcon.ActivationReason.DoubleClick: if reason == QSystemTrayIcon.ActivationReason.DoubleClick:
self.toggle_overlay() self.toggle_overlay()
def show_overlay(self): def show_overlay(self):
"""Show the overlay.""" """Show overlay."""
self.show() self.show()
self.raise_() self.raise_()
self.activateWindow() self.activateWindow()
@ -391,7 +361,7 @@ class OverlayWindow(QMainWindow):
print(f"Error in on_show for {plugin.name}: {e}") print(f"Error in on_show for {plugin.name}: {e}")
def hide_overlay(self): def hide_overlay(self):
"""Hide the overlay.""" """Hide overlay."""
self.hide() self.hide()
self.is_visible = False self.is_visible = False
self.visibility_changed.emit(False) self.visibility_changed.emit(False)
@ -404,14 +374,14 @@ class OverlayWindow(QMainWindow):
print(f"Error in on_hide for {plugin.name}: {e}") print(f"Error in on_hide for {plugin.name}: {e}")
def toggle_overlay(self): def toggle_overlay(self):
"""Toggle overlay visibility.""" """Toggle overlay."""
if self.is_visible: if self.is_visible:
self.hide_overlay() self.hide_overlay()
else: else:
self.show_overlay() self.show_overlay()
def quit_app(self): def quit_app(self):
"""Quit the application.""" """Quit application."""
if self.plugin_manager: if self.plugin_manager:
self.plugin_manager.shutdown_all() self.plugin_manager.shutdown_all()
@ -419,7 +389,7 @@ class OverlayWindow(QMainWindow):
QApplication.quit() QApplication.quit()
def keyPressEvent(self, event): def keyPressEvent(self, event):
"""Handle key presses.""" """Handle ESC key."""
if event.key() == Qt.Key.Key_Escape: if event.key() == Qt.Key.Key_Escape:
self.hide_overlay() self.hide_overlay()
else: else:

View File

@ -0,0 +1,7 @@
"""
Dashboard Plugin
"""
from .plugin import DashboardPlugin
__all__ = ["DashboardPlugin"]

View File

@ -0,0 +1,230 @@
"""
EU-Utility - Dashboard Plugin
Main dashboard with customizable widgets and overview.
"""
from PyQt6.QtWidgets import (
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
QPushButton, QGridLayout, QFrame, QScrollArea,
QSizePolicy
)
from PyQt6.QtCore import Qt, QTimer
from PyQt6.QtGui import QColor
from core.eu_styles import EU_COLORS, EU_STYLES
from plugins.base_plugin import BasePlugin
class DashboardPlugin(BasePlugin):
"""Main dashboard with overview of all features."""
name = "Dashboard"
version = "1.0.0"
author = "ImpulsiveFPS"
description = "Overview dashboard with widgets"
hotkey = "ctrl+shift+home"
def initialize(self):
"""Setup dashboard."""
self.widgets = []
def get_ui(self):
"""Create dashboard UI."""
widget = QWidget()
widget.setStyleSheet("background: transparent;")
# Main scroll area
scroll = QScrollArea()
scroll.setWidgetResizable(True)
scroll.setStyleSheet("""
QScrollArea {
background: transparent;
border: none;
}
QScrollBar:vertical {
background: rgba(0, 0, 0, 50);
width: 8px;
border-radius: 4px;
}
QScrollBar::handle:vertical {
background: rgba(255, 255, 255, 30);
border-radius: 4px;
}
""")
container = QWidget()
layout = QVBoxLayout(container)
layout.setSpacing(15)
layout.setContentsMargins(0, 0, 0, 0)
# Welcome
welcome = QLabel("⚡ Welcome to EU-Utility")
welcome.setStyleSheet(f"""
color: {EU_COLORS['accent_orange']};
font-size: 18px;
font-weight: bold;
""")
layout.addWidget(welcome)
subtitle = QLabel("Your Entropia Universe companion")
subtitle.setStyleSheet(f"color: {EU_COLORS['text_muted']}; font-size: 12px;")
layout.addWidget(subtitle)
# Quick stats row
stats_layout = QHBoxLayout()
stats_layout.setSpacing(10)
stats = [
("💰 PED", "26.02", "Balance"),
("📊 Skills", "12", "Tracked"),
("🎁 Items", "98", "In Inventory"),
("🎯 DPP", "3.45", "Current"),
]
for icon, value, label in stats:
card = self._create_stat_card(icon, value, label)
stats_layout.addWidget(card)
layout.addLayout(stats_layout)
# Quick actions
actions_label = QLabel("Quick Actions")
actions_label.setStyleSheet(f"color: {EU_COLORS['text_secondary']}; font-size: 13px; font-weight: bold;")
layout.addWidget(actions_label)
actions_grid = QGridLayout()
actions_grid.setSpacing(10)
actions = [
("🔍 Search", "Search items, mobs, locations"),
("📸 Scan", "OCR scan game windows"),
("📊 Skills", "Track skill gains"),
("🎁 Loot", "Track hunting loot"),
("⛏️ Mine", "Track mining finds"),
("📈 Market", "Auction price tracking"),
]
for i, (name, desc) in enumerate(actions):
btn = self._create_action_button(name, desc)
actions_grid.addWidget(btn, i // 3, i % 3)
layout.addLayout(actions_grid)
# Recent activity
activity_label = QLabel("Recent Activity")
activity_label.setStyleSheet(f"color: {EU_COLORS['text_secondary']}; font-size: 13px; font-weight: bold; margin-top: 10px;")
layout.addWidget(activity_label)
activity_frame = QFrame()
activity_frame.setStyleSheet(f"""
QFrame {{
background-color: {EU_COLORS['bg_panel']};
border: 1px solid {EU_COLORS['border_subtle']};
border-radius: 6px;
}}
""")
activity_layout = QVBoxLayout(activity_frame)
activities = [
"✓ Scanned inventory - 26.02 PED",
"✓ Tracked skill gain: +5.2 Aim",
"✓ Recorded loot: Animal Hide (0.03 PED)",
"→ Mission progress: 12/100 Oratan",
]
for activity in activities:
lbl = QLabel(activity)
lbl.setStyleSheet(f"color: {EU_COLORS['text_secondary']}; font-size: 11px; padding: 4px 0;")
activity_layout.addWidget(lbl)
layout.addWidget(activity_frame)
# Tips
tips_frame = QFrame()
tips_frame.setStyleSheet(f"""
QFrame {{
background-color: rgba(255, 140, 66, 20);
border: 1px solid rgba(255, 140, 66, 60);
border-radius: 6px;
}}
""")
tips_layout = QVBoxLayout(tips_frame)
tip_title = QLabel("💡 Pro Tip")
tip_title.setStyleSheet(f"color: {EU_COLORS['accent_orange']}; font-weight: bold; font-size: 11px;")
tips_layout.addWidget(tip_title)
tip_text = QLabel("Press Ctrl+Shift+U anytime to toggle this overlay. Use Ctrl+Shift+H to hide all widgets.")
tip_text.setStyleSheet(f"color: {EU_COLORS['text_secondary']}; font-size: 11px;")
tip_text.setWordWrap(True)
tips_layout.addWidget(tip_text)
layout.addWidget(tips_frame)
layout.addStretch()
scroll.setWidget(container)
main_layout = QVBoxLayout(widget)
main_layout.setContentsMargins(0, 0, 0, 0)
main_layout.addWidget(scroll)
return widget
def _create_stat_card(self, icon, value, label):
"""Create a stat card widget."""
card = QFrame()
card.setStyleSheet(f"""
QFrame {{
background-color: {EU_COLORS['bg_panel']};
border: 1px solid {EU_COLORS['border_subtle']};
border-radius: 6px;
}}
""")
layout = QVBoxLayout(card)
layout.setContentsMargins(12, 10, 12, 10)
layout.setSpacing(4)
value_lbl = QLabel(f"{icon} {value}")
value_lbl.setStyleSheet(f"color: {EU_COLORS['accent_orange']}; font-size: 16px; font-weight: bold;")
layout.addWidget(value_lbl)
label_lbl = QLabel(label)
label_lbl.setStyleSheet(f"color: {EU_COLORS['text_muted']}; font-size: 10px;")
layout.addWidget(label_lbl)
return card
def _create_action_button(self, name, description):
"""Create an action button."""
btn = QPushButton()
btn.setFixedHeight(60)
btn.setStyleSheet(f"""
QPushButton {{
background-color: {EU_COLORS['bg_panel']};
border: 1px solid {EU_COLORS['border_subtle']};
border-radius: 6px;
text-align: left;
padding: 10px;
}}
QPushButton:hover {{
background-color: {EU_COLORS['bg_hover']};
border: 1px solid {EU_COLORS['border_orange']};
}}
""")
# Layout for button content
btn_widget = QWidget()
btn_layout = QVBoxLayout(btn_widget)
btn_layout.setContentsMargins(0, 0, 0, 0)
btn_layout.setSpacing(2)
name_lbl = QLabel(name)
name_lbl.setStyleSheet(f"color: white; font-weight: bold; font-size: 12px;")
btn_layout.addWidget(name_lbl)
desc_lbl = QLabel(description)
desc_lbl.setStyleSheet(f"color: {EU_COLORS['text_muted']}; font-size: 9px;")
btn_layout.addWidget(desc_lbl)
return btn