From 7f6547f8de8bbbbd7e9bf13d8fd8054693a14422 Mon Sep 17 00:00:00 2001 From: LemonNexus Date: Fri, 13 Feb 2026 17:47:16 +0000 Subject: [PATCH] fix: Box-in-box UI, add Settings button to header FIXES: - Removed margins causing 'box in a box' effect - Removed inner container border (window has OS frame) - Window now fills properly without nested boxes NEW: - Added Settings button to header (always accessible) - Settings dialog shows all available plugins - Check/uncheck to enable/disable - Shows message when no plugins enabled - Save & Apply reloads plugins immediately --- projects/EU-Utility/core/overlay_window.py | 223 ++++++++++++++++++--- 1 file changed, 199 insertions(+), 24 deletions(-) diff --git a/projects/EU-Utility/core/overlay_window.py b/projects/EU-Utility/core/overlay_window.py index e6b332e..0125f3b 100644 --- a/projects/EU-Utility/core/overlay_window.py +++ b/projects/EU-Utility/core/overlay_window.py @@ -55,11 +55,18 @@ class OverlayWindow(QMainWindow): """Configure window - resizable and shows in taskbar.""" self.setWindowTitle("EU-Utility") - # Resizable window (no FramelessWindowHint), stays on top + # Resizable window, stays on top self.setWindowFlags( Qt.WindowType.WindowStaysOnTopHint ) + # Apply dark background to window itself + self.setStyleSheet(f""" + QMainWindow {{ + background-color: {EU_COLORS['bg_dark']}; + }} + """) + self.setMinimumSize(600, 400) self.resize(850, 600) self._center_window() @@ -72,69 +79,80 @@ class OverlayWindow(QMainWindow): self.move(x, y) def _setup_ui(self): - """Setup clean EU-styled UI.""" + """Setup clean EU-styled UI - fills window, no box-in-box.""" central = QWidget() self.setCentralWidget(central) + # NO MARGINS - fill entire window layout = QVBoxLayout(central) - layout.setContentsMargins(20, 20, 20, 20) + layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) - # Main container with orange window border like Skills window + # Main container - NO BORDER (window already has frame), fills window self.container = QFrame() self.container.setObjectName("euContainer") self.container.setStyleSheet(f""" #euContainer {{ background-color: {EU_COLORS['bg_dark']}; - border: 1px solid {EU_COLORS['border_window']}; - border-radius: 8px; + border: none; }} """) - shadow = QGraphicsDropShadowEffect() - shadow.setBlurRadius(30) - shadow.setColor(QColor(0, 0, 0, 150)) - shadow.setOffset(0, 8) - self.container.setGraphicsEffect(shadow) - container_layout = QVBoxLayout(self.container) container_layout.setContentsMargins(0, 0, 0, 0) container_layout.setSpacing(0) - # Header with centered title - CLEAN (no weird lines) + # Header with settings button header = QWidget() header.setObjectName("header") header.setStyleSheet(f""" - QWidget {{ + QWidget#header {{ background-color: {EU_COLORS['bg_header']}; - border-top-left-radius: 8px; - border-top-right-radius: 8px; border-bottom: 1px solid {EU_COLORS['border_medium']}; }} """) header_layout = QHBoxLayout(header) - header_layout.setContentsMargins(15, 12, 15, 12) + header_layout.setContentsMargins(15, 8, 15, 8) header_layout.setSpacing(10) # App icon app_icon = QLabel() - app_icon_pixmap = self.icon_manager.get_pixmap("target", size=20) + app_icon_pixmap = self.icon_manager.get_pixmap("target", size=18) app_icon.setPixmap(app_icon_pixmap) - app_icon.setFixedSize(20, 20) + app_icon.setFixedSize(18, 18) header_layout.addWidget(app_icon) - # Centered title - title = QLabel("EU-UTILITY") + # Title + title = QLabel("EU-Utility") title.setStyleSheet(f""" color: {EU_COLORS['text_primary']}; - font-size: 14px; + font-size: 13px; font-weight: bold; - letter-spacing: 2px; """) header_layout.addWidget(title) header_layout.addStretch() + # Settings button (always accessible) + settings_btn = QPushButton("Settings") + settings_btn.setStyleSheet(f""" + QPushButton {{ + background-color: {EU_COLORS['bg_panel']}; + color: {EU_COLORS['text_secondary']}; + border: 1px solid {EU_COLORS['border_subtle']}; + border-radius: 4px; + padding: 4px 12px; + font-size: 11px; + }} + QPushButton:hover {{ + background-color: {EU_COLORS['bg_hover']}; + border-color: {EU_COLORS['accent_orange']}; + color: {EU_COLORS['text_primary']}; + }} + """) + settings_btn.clicked.connect(self._open_settings) + header_layout.addWidget(settings_btn) + # View toggle buttons view_group = QButtonGroup(self) view_group.setExclusive(True) @@ -142,7 +160,7 @@ class OverlayWindow(QMainWindow): self.icon_view_btn = QPushButton("Grid") self.icon_view_btn.setCheckable(True) self.icon_view_btn.setChecked(True) - self.icon_view_btn.setFixedSize(50, 24) + self.icon_view_btn.setFixedSize(45, 22) self.icon_view_btn.setStyleSheet(self._view_toggle_style()) self.icon_view_btn.clicked.connect(lambda: self._set_view_mode("icons")) header_layout.addWidget(self.icon_view_btn) @@ -336,6 +354,19 @@ class OverlayWindow(QMainWindow): """Load plugins into sidebar and stack - FIXED indexing.""" plugins_list = list(self.plugin_manager.get_all_plugins().items()) + # Show message if no plugins enabled + if not plugins_list: + # Add placeholder message + placeholder = QLabel("No plugins enabled\n\nClick Settings to enable plugins") + placeholder.setAlignment(Qt.AlignmentFlag.AlignCenter) + placeholder.setStyleSheet(f""" + color: {EU_COLORS['text_muted']}; + font-size: 14px; + padding: 40px; + """) + self.plugin_stack.addWidget(placeholder) + return + for idx, (plugin_id, plugin) in enumerate(plugins_list): # Get icon name icon_name = get_plugin_icon_name(plugin.name) @@ -534,6 +565,150 @@ class OverlayWindow(QMainWindow): self.tray_icon.hide() QApplication.quit() + def _open_settings(self): + """Open Settings dialog - works even when Settings plugin not loaded.""" + from PyQt6.QtWidgets import QDialog, QVBoxLayout, QTabWidget, QListWidget, QListWidgetItem, QCheckBox, QHBoxLayout + + dialog = QDialog(self) + dialog.setWindowTitle("EU-Utility Settings") + dialog.setMinimumSize(500, 400) + dialog.setStyleSheet(f""" + QDialog {{ + background-color: {EU_COLORS['bg_dark']}; + color: white; + }} + QLabel {{ + color: white; + }} + QTabWidget::pane {{ + background-color: {EU_COLORS['bg_panel']}; + border: 1px solid {EU_COLORS['border_medium']}; + border-radius: 6px; + }} + QTabBar::tab {{ + background-color: {EU_COLORS['bg_header']}; + color: {EU_COLORS['text_secondary']}; + padding: 10px 20px; + border-top-left-radius: 6px; + border-top-right-radius: 6px; + }} + QTabBar::tab:selected {{ + background-color: {EU_COLORS['accent_orange']}; + color: white; + font-weight: bold; + }} + """) + + layout = QVBoxLayout(dialog) + + tabs = QTabWidget() + + # Plugins tab + plugins_tab = QWidget() + plugins_layout = QVBoxLayout(plugins_tab) + + info = QLabel("Check plugins to enable them:") + info.setStyleSheet(f"color: {EU_COLORS['text_muted']};") + plugins_layout.addWidget(info) + + self.settings_checkboxes = {} + + if self.plugin_manager: + all_plugins = self.plugin_manager.get_all_discovered_plugins() + sorted_plugins = sorted(all_plugins.items(), key=lambda x: x[1].name) + + for plugin_id, plugin_class in sorted_plugins: + row = QHBoxLayout() + + cb = QCheckBox(f"{plugin_class.name} (v{plugin_class.version})") + cb.setChecked(self.plugin_manager.is_plugin_enabled(plugin_id)) + cb.setStyleSheet("color: white;") + self.settings_checkboxes[plugin_id] = cb + row.addWidget(cb) + + desc = QLabel(plugin_class.description) + desc.setStyleSheet(f"color: {EU_COLORS['text_muted']}; font-size: 11px;") + row.addWidget(desc, 1) + + plugins_layout.addLayout(row) + + plugins_layout.addStretch() + + # Buttons + btn_layout = QHBoxLayout() + + save_btn = QPushButton("Save & Apply") + save_btn.setStyleSheet(f""" + QPushButton {{ + background-color: {EU_COLORS['accent_orange']}; + color: white; + padding: 10px 20px; + border: none; + border-radius: 4px; + font-weight: bold; + }} + """) + save_btn.clicked.connect(lambda: self._save_plugin_settings(dialog)) + btn_layout.addWidget(save_btn) + + btn_layout.addStretch() + plugins_layout.addLayout(btn_layout) + + tabs.addTab(plugins_tab, "Plugins") + + # Add info tab + info_tab = QWidget() + info_layout = QVBoxLayout(info_tab) + info_text = QLabel( + "EU-Utility v2.0\n\n" + "Loaded plugins will appear in the sidebar.\n" + "Enable plugins above to use them.\n\n" + "Hotkeys:\n" + "Ctrl+Shift+U - Toggle overlay\n" + "Ctrl+Shift+H - Hide all overlays" + ) + info_text.setStyleSheet("color: white;") + info_layout.addWidget(info_text) + info_layout.addStretch() + tabs.addTab(info_tab, "About") + + layout.addWidget(tabs) + dialog.exec() + + def _save_plugin_settings(self, dialog): + """Save plugin enable/disable settings.""" + if not self.plugin_manager: + return + + for plugin_id, cb in self.settings_checkboxes.items(): + if cb.isChecked(): + self.plugin_manager.enable_plugin(plugin_id) + else: + self.plugin_manager.disable_plugin(plugin_id) + + # Reload plugins in UI + self._reload_plugins() + dialog.accept() + + def _reload_plugins(self): + """Reload plugins in UI after enabling/disabling.""" + # Clear existing + while self.plugin_stack.count() > 0: + widget = self.plugin_stack.widget(0) + self.plugin_stack.removeWidget(widget) + + self.plugin_list.clear() + self.plugin_buttons = [] + + # Clear icon grid + while self.icon_grid_layout.count(): + item = self.icon_grid_layout.takeAt(0) + if item.widget(): + item.widget().deleteLater() + + # Reload + self._load_plugins() + def keyPressEvent(self, event): """Handle ESC key.""" if event.key() == Qt.Key.Key_Escape: