feat: Spotlight-style overlay redesign

- Frosted glass effect with transparency
- Rounded corners (20px radius)
- Header bar with search icon
- Circular plugin icon buttons at bottom
- Emoji icons instead of text labels
- Subtle shadows and highlights
- macOS-style aesthetic
- Plugins now sit on transparent background
This commit is contained in:
LemonNexus 2026-02-13 13:03:42 +00:00
parent 5e08f56fb2
commit 8dbbf4d971
1 changed files with 154 additions and 59 deletions

View File

@ -1,7 +1,7 @@
""" """
EU-Utility - Overlay Window EU-Utility - Overlay Window
Transparent, always-on-top overlay for in-game use. Spotlight-style overlay with frosted glass effect.
""" """
import sys import sys
@ -12,10 +12,10 @@ try:
from PyQt6.QtWidgets import ( from PyQt6.QtWidgets import (
QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
QLabel, QPushButton, QStackedWidget, QSystemTrayIcon, QLabel, QPushButton, QStackedWidget, QSystemTrayIcon,
QMenu, QApplication, QFrame QMenu, QApplication, QFrame, QGraphicsDropShadowEffect
) )
from PyQt6.QtCore import Qt, QTimer, pyqtSignal from PyQt6.QtCore import Qt, QTimer, pyqtSignal, QSize
from PyQt6.QtGui import QAction, QIcon, QKeySequence, QShortcut from PyQt6.QtGui import QAction, QIcon, QColor, QFont
PYQT6_AVAILABLE = True PYQT6_AVAILABLE = True
except ImportError: except ImportError:
PYQT6_AVAILABLE = False PYQT6_AVAILABLE = False
@ -23,7 +23,7 @@ except ImportError:
class OverlayWindow(QMainWindow): class OverlayWindow(QMainWindow):
"""Transparent overlay window for in-game use.""" """Spotlight-style overlay window."""
visibility_changed = pyqtSignal(bool) visibility_changed = pyqtSignal(bool)
@ -35,6 +35,7 @@ class OverlayWindow(QMainWindow):
self.plugin_manager = plugin_manager self.plugin_manager = plugin_manager
self.is_visible = False self.is_visible = False
self.plugin_buttons = []
self._setup_window() self._setup_window()
self._setup_ui() self._setup_ui()
@ -57,22 +58,19 @@ class OverlayWindow(QMainWindow):
# Transparent background # Transparent background
self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground)
# Size and position # Size and position - wider but shorter for spotlight style
self.resize(800, 600) self.resize(700, 500)
self._center_window() self._center_window()
# Click-through when inactive (optional)
# self.setAttribute(Qt.WidgetAttribute.WA_TransparentForMouseEvents)
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()) // 2 y = (screen.height() - self.height()) // 3 # Upper third like Spotlight
self.move(x, y) self.move(x, y)
def _setup_ui(self): def _setup_ui(self):
"""Setup the user interface.""" """Setup the Spotlight-style UI."""
# Central widget # Central widget
central = QWidget() central = QWidget()
self.setCentralWidget(central) self.setCentralWidget(central)
@ -80,101 +78,186 @@ class OverlayWindow(QMainWindow):
# Main layout # Main layout
layout = QVBoxLayout(central) layout = QVBoxLayout(central)
layout.setContentsMargins(20, 20, 20, 20) layout.setContentsMargins(20, 20, 20, 20)
layout.setSpacing(0)
# Container with background # Frosted glass container
self.container = QFrame() self.container = QFrame()
self.container.setObjectName("overlayContainer") self.container.setObjectName("spotlightContainer")
self.container.setStyleSheet(""" self.container.setStyleSheet("""
#overlayContainer { #spotlightContainer {
background-color: rgba(30, 30, 30, 240); background-color: rgba(40, 40, 40, 180);
border-radius: 10px; border-radius: 20px;
border: 2px solid #444; border: 1px solid rgba(255, 255, 255, 30);
} }
""") """)
# Add shadow effect
shadow = QGraphicsDropShadowEffect()
shadow.setBlurRadius(30)
shadow.setColor(QColor(0, 0, 0, 100))
shadow.setOffset(0, 10)
self.container.setGraphicsEffect(shadow)
container_layout = QVBoxLayout(self.container) container_layout = QVBoxLayout(self.container)
container_layout.setContentsMargins(15, 15, 15, 15) container_layout.setContentsMargins(0, 0, 0, 0)
container_layout.setSpacing(0)
# Header # Header with search bar style
header = QHBoxLayout() header = QWidget()
header.setStyleSheet("""
QWidget {
background-color: rgba(255, 255, 255, 15);
border-top-left-radius: 20px;
border-top-right-radius: 20px;
border-bottom: 1px solid rgba(255, 255, 255, 20);
}
""")
header_layout = QHBoxLayout(header)
header_layout.setContentsMargins(20, 15, 20, 15)
header_layout.setSpacing(15)
# Search icon
search_icon = QLabel("🔍")
search_icon.setStyleSheet("font-size: 18px; background: transparent; border: none;")
header_layout.addWidget(search_icon)
# Title/Search label
title = QLabel("EU-Utility") title = QLabel("EU-Utility")
title.setStyleSheet(""" title.setStyleSheet("""
color: #fff; color: rgba(255, 255, 255, 200);
font-size: 18px; font-size: 16px;
font-weight: bold; font-weight: 500;
background: transparent;
border: none;
""") """)
header.addWidget(title) header_layout.addWidget(title)
header.addStretch() header_layout.addStretch()
# Close button # Close button (X) - subtle
close_btn = QPushButton("×") close_btn = QPushButton("×")
close_btn.setFixedSize(30, 30) close_btn.setFixedSize(28, 28)
close_btn.setStyleSheet(""" close_btn.setStyleSheet("""
QPushButton { QPushButton {
background-color: transparent; background-color: transparent;
color: #999; color: rgba(255, 255, 255, 150);
font-size: 20px; font-size: 20px;
font-weight: 300;
border: none; border: none;
border-radius: 15px; border-radius: 14px;
} }
QPushButton:hover { QPushButton:hover {
background-color: #c44; background-color: rgba(255, 255, 255, 20);
color: white; color: white;
} }
""") """)
close_btn.clicked.connect(self.hide_overlay) close_btn.clicked.connect(self.hide_overlay)
header.addWidget(close_btn) header_layout.addWidget(close_btn)
container_layout.addLayout(header) container_layout.addWidget(header)
# Plugin tabs / stack # Plugin content area
self.content_area = QWidget()
self.content_area.setStyleSheet("background: transparent;")
content_layout = QVBoxLayout(self.content_area)
content_layout.setContentsMargins(20, 20, 20, 20)
content_layout.setSpacing(15)
# Plugin stack
self.plugin_stack = QStackedWidget() self.plugin_stack = QStackedWidget()
container_layout.addWidget(self.plugin_stack) self.plugin_stack.setStyleSheet("background: transparent;")
content_layout.addWidget(self.plugin_stack, 1)
# Plugin buttons container_layout.addWidget(self.content_area, 1)
# Plugin selector bar (at bottom)
if self.plugin_manager: if self.plugin_manager:
self._setup_plugin_buttons(container_layout) self._setup_plugin_bar(container_layout)
layout.addWidget(self.container) layout.addWidget(self.container)
def _setup_plugin_buttons(self, layout): def _setup_plugin_bar(self, layout):
"""Setup buttons to switch between plugins.""" """Setup circular plugin icon bar at bottom."""
btn_layout = QHBoxLayout() bar = QWidget()
bar.setStyleSheet("""
for plugin_id, plugin in self.plugin_manager.get_all_plugins().items(): QWidget {
btn = QPushButton(plugin.name) background-color: rgba(0, 0, 0, 40);
btn.setStyleSheet(""" border-bottom-left-radius: 20px;
QPushButton { border-bottom-right-radius: 20px;
background-color: #333;
color: white;
padding: 8px 16px;
border-radius: 4px;
border: none;
}
QPushButton:hover {
background-color: #444;
}
QPushButton:pressed {
background-color: #555;
} }
""") """)
bar_layout = QHBoxLayout(bar)
bar_layout.setContentsMargins(20, 12, 20, 12)
bar_layout.setSpacing(15)
bar_layout.addStretch()
# Add plugin icons
plugin_icons = {
"Universal Search": "🔍",
"Calculator": "🧮",
"Spotify": "🎵",
"Nexus Search": "🌐",
}
for idx, (plugin_id, plugin) in enumerate(self.plugin_manager.get_all_plugins().items()):
btn = QPushButton()
# Get icon emoji or default
icon = plugin_icons.get(plugin.name, "")
btn.setText(icon)
btn.setFixedSize(44, 44)
btn.setStyleSheet("""
QPushButton {
background-color: rgba(255, 255, 255, 15);
color: white;
font-size: 20px;
border: none;
border-radius: 22px;
}
QPushButton:hover {
background-color: rgba(255, 255, 255, 30);
}
QPushButton:checked, QPushButton:pressed {
background-color: #4a9eff;
}
""")
btn.setCheckable(True)
btn.setToolTip(plugin.name)
# Add plugin UI to stack # Add plugin UI to stack
try: try:
plugin_ui = plugin.get_ui() plugin_ui = plugin.get_ui()
if plugin_ui: if plugin_ui:
plugin_ui.setStyleSheet("background: transparent;")
self.plugin_stack.addWidget(plugin_ui) self.plugin_stack.addWidget(plugin_ui)
btn.clicked.connect( btn.clicked.connect(
lambda checked, idx=self.plugin_stack.count()-1: lambda checked, i=idx, b=btn: self._switch_plugin(i, b)
self.plugin_stack.setCurrentIndex(idx)
) )
btn_layout.addWidget(btn) self.plugin_buttons.append(btn)
bar_layout.addWidget(btn)
except Exception as e: except Exception as e:
print(f"Error loading UI for {plugin.name}: {e}") print(f"Error loading UI for {plugin.name}: {e}")
btn_layout.addStretch() bar_layout.addStretch()
layout.addLayout(btn_layout) layout.addWidget(bar)
# Select first plugin by default
if self.plugin_buttons:
self.plugin_buttons[0].setChecked(True)
def _switch_plugin(self, index, button):
"""Switch to selected plugin."""
# Uncheck all other buttons
for btn in self.plugin_buttons:
if btn != button:
btn.setChecked(False)
# Ensure clicked button stays checked
button.setChecked(True)
# Switch stack
self.plugin_stack.setCurrentIndex(index)
def _setup_tray(self): def _setup_tray(self):
"""Setup system tray icon.""" """Setup system tray icon."""
@ -187,18 +270,30 @@ class OverlayWindow(QMainWindow):
# Tray menu # Tray menu
tray_menu = QMenu() tray_menu = QMenu()
tray_menu.setStyleSheet("""
QMenu {
background-color: rgba(40, 40, 40, 240);
color: white;
border: 1px solid rgba(255, 255, 255, 30);
border-radius: 8px;
padding: 8px;
}
QMenu::item {
padding: 8px 20px;
border-radius: 4px;
}
QMenu::item:selected {
background-color: #4a9eff;
}
""")
show_action = QAction("Show", 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)
hide_action = QAction("Hide", self)
hide_action.triggered.connect(self.hide_overlay)
tray_menu.addAction(hide_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)