""" EU-Utility - Settings UI Plugin Settings menu for configuring EU-Utility. """ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QCheckBox, QLineEdit, QComboBox, QSlider, QTabWidget, QGroupBox, QListWidget, QListWidgetItem, QFrame, QFileDialog, QScrollArea ) from PyQt6.QtCore import Qt, QTimer from core.settings import get_settings from plugins.base_plugin import BasePlugin class SettingsPlugin(BasePlugin): """EU-Utility settings and configuration.""" name = "Settings" version = "1.0.0" author = "ImpulsiveFPS" description = "Configure EU-Utility preferences" hotkey = "ctrl+shift+comma" def initialize(self): """Setup settings.""" self.settings = get_settings() def get_ui(self): """Create settings UI.""" widget = QWidget() widget.setStyleSheet("background: transparent;") layout = QVBoxLayout(widget) layout.setSpacing(10) layout.setContentsMargins(0, 0, 0, 0) # Title title = QLabel("Settings") title.setStyleSheet("color: white; font-size: 16px; font-weight: bold;") layout.addWidget(title) # Tabs tabs = QTabWidget() tabs.setStyleSheet(""" QTabBar::tab { background-color: rgba(35, 40, 55, 200); color: rgba(255,255,255,150); padding: 10px 20px; border-top-left-radius: 6px; border-top-right-radius: 6px; } QTabBar::tab:selected { background-color: #ff8c42; color: white; font-weight: bold; } """) # General tab general_tab = self._create_general_tab() tabs.addTab(general_tab, "General") # Plugins tab plugins_tab = self._create_plugins_tab() tabs.addTab(plugins_tab, "Plugins") # Hotkeys tab hotkeys_tab = self._create_hotkeys_tab() tabs.addTab(hotkeys_tab, "Hotkeys") # Overlay tab overlay_tab = self._create_overlay_tab() tabs.addTab(overlay_tab, "Overlays") # Data tab data_tab = self._create_data_tab() tabs.addTab(data_tab, "Data") layout.addWidget(tabs) # Save/Reset buttons btn_layout = QHBoxLayout() save_btn = QPushButton("Save Settings") save_btn.setStyleSheet(""" QPushButton { background-color: #4caf50; color: white; padding: 10px 20px; border: none; border-radius: 4px; font-weight: bold; } """) save_btn.clicked.connect(self._save_settings) btn_layout.addWidget(save_btn) reset_btn = QPushButton("Reset to Default") reset_btn.setStyleSheet(""" QPushButton { background-color: rgba(255,255,255,20); color: white; padding: 10px 20px; border: none; border-radius: 4px; } """) reset_btn.clicked.connect(self._reset_settings) btn_layout.addWidget(reset_btn) btn_layout.addStretch() layout.addLayout(btn_layout) return widget def _create_general_tab(self): """Create general settings tab.""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Appearance appear_group = QGroupBox("Appearance") appear_group.setStyleSheet(self._group_style()) appear_layout = QVBoxLayout(appear_group) # Theme theme_layout = QHBoxLayout() theme_layout.addWidget(QLabel("Theme:")) self.theme_combo = QComboBox() self.theme_combo.addItems(["Dark (EU Style)", "Light", "Auto"]) self.theme_combo.setCurrentText(self.settings.get('theme', 'Dark (EU Style)')) theme_layout.addWidget(self.theme_combo) theme_layout.addStretch() appear_layout.addLayout(theme_layout) # Opacity opacity_layout = QHBoxLayout() opacity_layout.addWidget(QLabel("Overlay Opacity:")) self.opacity_slider = QSlider(Qt.Orientation.Horizontal) self.opacity_slider.setMinimum(50) self.opacity_slider.setMaximum(100) self.opacity_slider.setValue(int(self.settings.get('overlay_opacity', 0.9) * 100)) opacity_layout.addWidget(self.opacity_slider) self.opacity_label = QLabel(f"{self.opacity_slider.value()}%") opacity_layout.addWidget(self.opacity_label) opacity_layout.addStretch() appear_layout.addLayout(opacity_layout) # Icon size icon_layout = QHBoxLayout() icon_layout.addWidget(QLabel("Icon Size:")) self.icon_combo = QComboBox() self.icon_combo.addItems(["Small (20px)", "Medium (24px)", "Large (32px)"]) icon_layout.addWidget(self.icon_combo) icon_layout.addStretch() appear_layout.addLayout(icon_layout) layout.addWidget(appear_group) # Behavior behavior_group = QGroupBox("Behavior") behavior_group.setStyleSheet(self._group_style()) behavior_layout = QVBoxLayout(behavior_group) self.auto_start_cb = QCheckBox("Start with Windows") self.auto_start_cb.setChecked(self.settings.get('auto_start', False)) behavior_layout.addWidget(self.auto_start_cb) self.minimize_cb = QCheckBox("Minimize to tray on close") self.minimize_cb.setChecked(self.settings.get('minimize_to_tray', True)) behavior_layout.addWidget(self.minimize_cb) self.tooltips_cb = QCheckBox("Show tooltips") self.tooltips_cb.setChecked(self.settings.get('show_tooltips', True)) behavior_layout.addWidget(self.tooltips_cb) layout.addWidget(behavior_group) layout.addStretch() return tab def _create_plugins_tab(self): """Create plugins management tab - enable/disable plugins.""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) # Info label info = QLabel("Check plugins to enable them. Uncheck to disable. Changes take effect immediately.") info.setStyleSheet("color: rgba(255,255,255,150);") layout.addWidget(info) # Scroll area for plugin list scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setFrameShape(QFrame.Shape.NoFrame) scroll.setStyleSheet("background: transparent; border: none;") scroll_content = QWidget() plugins_layout = QVBoxLayout(scroll_content) plugins_layout.setSpacing(8) plugins_layout.setAlignment(Qt.AlignmentFlag.AlignTop) self.plugin_checkboxes = {} # Get all discovered plugins from plugin manager if hasattr(self.overlay, 'plugin_manager'): plugin_manager = self.overlay.plugin_manager all_plugins = plugin_manager.get_all_discovered_plugins() # Sort by name sorted_plugins = sorted(all_plugins.items(), key=lambda x: x[1].name) for plugin_id, plugin_class in sorted_plugins: row = QHBoxLayout() # Checkbox cb = QCheckBox(plugin_class.name) cb.setChecked(plugin_manager.is_plugin_enabled(plugin_id)) cb.setStyleSheet(""" QCheckBox { color: white; spacing: 8px; } QCheckBox::indicator { width: 18px; height: 18px; } """) # Connect to enable/disable cb.stateChanged.connect( lambda state, pid=plugin_id: self._toggle_plugin(pid, state == Qt.CheckState.Checked.value) ) self.plugin_checkboxes[plugin_id] = cb row.addWidget(cb) # Version version_label = QLabel(f"v{plugin_class.version}") version_label.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;") row.addWidget(version_label) # Description desc_label = QLabel(f"- {plugin_class.description}") desc_label.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;") desc_label.setWordWrap(True) row.addWidget(desc_label, 1) row.addStretch() plugins_layout.addLayout(row) # Separator sep = QFrame() sep.setFrameShape(QFrame.Shape.HLine) sep.setStyleSheet("background-color: rgba(100, 110, 130, 40);") sep.setFixedHeight(1) plugins_layout.addWidget(sep) plugins_layout.addStretch() scroll.setWidget(scroll_content) layout.addWidget(scroll) # Buttons btn_layout = QHBoxLayout() enable_all_btn = QPushButton("Enable All") enable_all_btn.setStyleSheet(""" QPushButton { background-color: #4caf50; color: white; padding: 8px 16px; border: none; border-radius: 4px; } """) enable_all_btn.clicked.connect(self._enable_all_plugins) btn_layout.addWidget(enable_all_btn) disable_all_btn = QPushButton("Disable All") disable_all_btn.setStyleSheet(""" QPushButton { background-color: rgba(255,255,255,20); color: white; padding: 8px 16px; border: none; border-radius: 4px; } """) disable_all_btn.clicked.connect(self._disable_all_plugins) btn_layout.addWidget(disable_all_btn) btn_layout.addStretch() layout.addLayout(btn_layout) return tab def _create_hotkeys_tab(self): """Create hotkeys configuration tab.""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) hotkeys_group = QGroupBox("Global Hotkeys") hotkeys_group.setStyleSheet(self._group_style()) hotkeys_layout = QVBoxLayout(hotkeys_group) self.hotkey_inputs = {} hotkeys = [ ("Toggle Overlay", "hotkey_toggle", "ctrl+shift+u"), ("Universal Search", "hotkey_search", "ctrl+shift+f"), ("Calculator", "hotkey_calculator", "ctrl+shift+c"), ("Spotify", "hotkey_music", "ctrl+shift+m"), ("Game Reader", "hotkey_scan", "ctrl+shift+r"), ("Skill Scanner", "hotkey_skills", "ctrl+shift+s"), ] for label, key, default in hotkeys: row = QHBoxLayout() row.addWidget(QLabel(label + ":")) input_field = QLineEdit() input_field.setText(self.settings.get(key, default)) input_field.setStyleSheet(""" QLineEdit { background-color: rgba(30, 35, 45, 200); color: white; border: 1px solid rgba(100, 110, 130, 80); padding: 5px; min-width: 150px; } """) self.hotkey_inputs[key] = input_field row.addWidget(input_field) row.addStretch() hotkeys_layout.addLayout(row) layout.addWidget(hotkeys_group) layout.addStretch() return tab def _create_overlay_tab(self): """Create overlay widgets configuration tab.""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) overlays_group = QGroupBox("In-Game Overlays") overlays_group.setStyleSheet(self._group_style()) overlays_layout = QVBoxLayout(overlays_group) overlays = [ ("Spotify Player", "spotify", True), ("Mission Tracker", "mission", False), ("Skill Gains", "skillgain", False), ("DPP Tracker", "dpp", False), ] for name, key, enabled in overlays: cb = QCheckBox(name) cb.setChecked(enabled) overlays_layout.addWidget(cb) # Reset positions reset_pos_btn = QPushButton("↺ Reset All Positions") reset_pos_btn.setStyleSheet(""" QPushButton { background-color: rgba(255,255,255,20); color: white; padding: 8px; border: none; border-radius: 4px; } """) overlays_layout.addWidget(reset_pos_btn) layout.addWidget(overlays_group) layout.addStretch() return tab def _create_data_tab(self): """Create data management tab.""" tab = QWidget() layout = QVBoxLayout(tab) layout.setSpacing(15) data_group = QGroupBox("Data Management") data_group.setStyleSheet(self._group_style()) data_layout = QVBoxLayout(data_group) # Export export_btn = QPushButton("📤 Export All Data") export_btn.setStyleSheet(""" QPushButton { background-color: #4a9eff; color: white; padding: 10px; border: none; border-radius: 4px; font-weight: bold; } """) export_btn.clicked.connect(self._export_data) data_layout.addWidget(export_btn) # Import import_btn = QPushButton("📥 Import Data") import_btn.setStyleSheet(""" QPushButton { background-color: rgba(255,255,255,20); color: white; padding: 10px; border: none; border-radius: 4px; } """) import_btn.clicked.connect(self._import_data) data_layout.addWidget(import_btn) # Clear clear_btn = QPushButton("Clear All Data") clear_btn.setStyleSheet(""" QPushButton { background-color: #f44336; color: white; padding: 10px; border: none; border-radius: 4px; } """) clear_btn.clicked.connect(self._clear_data) data_layout.addWidget(clear_btn) # Retention retention_layout = QHBoxLayout() retention_layout.addWidget(QLabel("Data retention:")) self.retention_combo = QComboBox() self.retention_combo.addItems(["7 days", "30 days", "90 days", "Forever"]) retention_layout.addWidget(self.retention_combo) retention_layout.addStretch() data_layout.addLayout(retention_layout) layout.addWidget(data_group) layout.addStretch() return tab def _group_style(self): """Get group box style.""" return """ QGroupBox { color: rgba(255,255,255,200); border: 1px solid rgba(100, 110, 130, 80); border-radius: 6px; margin-top: 10px; font-weight: bold; font-size: 12px; } QGroupBox::title { subcontrol-origin: margin; left: 10px; padding: 0 5px; } """ def _save_settings(self): """Save all settings.""" # General self.settings.set('theme', self.theme_combo.currentText()) self.settings.set('overlay_opacity', self.opacity_slider.value() / 100) self.settings.set('auto_start', self.auto_start_cb.isChecked()) self.settings.set('minimize_to_tray', self.minimize_cb.isChecked()) self.settings.set('show_tooltips', self.tooltips_cb.isChecked()) # Hotkeys for key, input_field in self.hotkey_inputs.items(): self.settings.set(key, input_field.text()) print("Settings saved!") def _reset_settings(self): """Reset to defaults.""" self.settings.reset() print("Settings reset to defaults!") def _export_data(self): """Export all data.""" from PyQt6.QtWidgets import QFileDialog filepath, _ = QFileDialog.getSaveFileName( None, "Export EU-Utility Data", "eu_utility_backup.json", "JSON (*.json)" ) if filepath: import shutil data_dir = Path("data") if data_dir.exists(): # Create export import json export_data = {} for f in data_dir.glob("*.json"): with open(f, 'r') as file: export_data[f.stem] = json.load(file) with open(filepath, 'w') as file: json.dump(export_data, file, indent=2) def _import_data(self): """Import data.""" pass def _clear_data(self): """Clear all data.""" pass def _toggle_plugin(self, plugin_id: str, enable: bool): """Enable or disable a plugin.""" if not hasattr(self.overlay, 'plugin_manager'): return plugin_manager = self.overlay.plugin_manager if enable: success = plugin_manager.enable_plugin(plugin_id) if success: print(f"[Settings] Enabled plugin: {plugin_id}") else: print(f"[Settings] Failed to enable plugin: {plugin_id}") else: success = plugin_manager.disable_plugin(plugin_id) if success: print(f"[Settings] Disabled plugin: {plugin_id}") # Notify user a restart might be needed for some changes # But we try to apply immediately def _enable_all_plugins(self): """Enable all plugins.""" if not hasattr(self.overlay, 'plugin_manager'): return plugin_manager = self.overlay.plugin_manager for plugin_id, cb in self.plugin_checkboxes.items(): cb.setChecked(True) plugin_manager.enable_plugin(plugin_id) def _disable_all_plugins(self): """Disable all plugins.""" if not hasattr(self.overlay, 'plugin_manager'): return plugin_manager = self.overlay.plugin_manager for plugin_id, cb in self.plugin_checkboxes.items(): cb.setChecked(False) plugin_manager.disable_plugin(plugin_id)