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:
parent
0228a641ed
commit
2abbea9563
|
|
@ -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():
|
||||||
|
|
@ -45,49 +47,34 @@ class IconHelper:
|
||||||
|
|
||||||
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:
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
"""
|
||||||
|
Dashboard Plugin
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .plugin import DashboardPlugin
|
||||||
|
|
||||||
|
__all__ = ["DashboardPlugin"]
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue