458 lines
16 KiB
Python
458 lines
16 KiB
Python
"""
|
|
EU-Utility - Settings Manager (Core Framework Component)
|
|
|
|
Built-in settings interface - not a plugin.
|
|
"""
|
|
|
|
from PyQt6.QtWidgets import (
|
|
QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
QPushButton, QCheckBox, QLineEdit, QComboBox,
|
|
QSlider, QTabWidget, QGroupBox, QFrame,
|
|
QFileDialog, QScrollArea, QMessageBox
|
|
)
|
|
from PyQt6.QtCore import Qt
|
|
|
|
from core.icon_manager import get_icon_manager
|
|
|
|
|
|
class SettingsView(QWidget):
|
|
"""Main settings interface - built into the framework."""
|
|
|
|
def __init__(self, overlay_window, parent=None):
|
|
super().__init__(parent)
|
|
self.overlay = overlay_window
|
|
self.settings = overlay_window.settings if hasattr(overlay_window, 'settings') else {}
|
|
self.plugin_manager = overlay_window.plugin_manager if hasattr(overlay_window, 'plugin_manager') else None
|
|
self.icon_manager = get_icon_manager()
|
|
|
|
self._setup_ui()
|
|
|
|
def _setup_ui(self):
|
|
"""Create the settings UI."""
|
|
layout = QVBoxLayout(self)
|
|
layout.setSpacing(16)
|
|
layout.setContentsMargins(24, 24, 24, 24)
|
|
|
|
# Header with icon
|
|
header_layout = QHBoxLayout()
|
|
header_layout.setSpacing(12)
|
|
|
|
header_icon = QLabel()
|
|
header_pixmap = self.icon_manager.get_pixmap("settings", size=28)
|
|
header_icon.setPixmap(header_pixmap)
|
|
header_layout.addWidget(header_icon)
|
|
|
|
header = QLabel("Settings")
|
|
header.setStyleSheet("font-size: 24px; font-weight: bold; color: white;")
|
|
header_layout.addWidget(header)
|
|
header_layout.addStretch()
|
|
|
|
layout.addLayout(header_layout)
|
|
|
|
# Tabs
|
|
self.tabs = QTabWidget()
|
|
self.tabs.setStyleSheet("""
|
|
QTabBar::tab {
|
|
background-color: rgba(20, 31, 35, 0.95);
|
|
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;
|
|
}
|
|
""")
|
|
|
|
# Add tabs
|
|
self.tabs.addTab(self._create_general_tab(), "General")
|
|
self.tabs.addTab(self._create_plugin_store_tab(), "Plugin Store")
|
|
self.tabs.addTab(self._create_plugins_tab(), "My Plugins")
|
|
self.tabs.addTab(self._create_hotkeys_tab(), "Hotkeys")
|
|
self.tabs.addTab(self._create_data_tab(), "Data & Backup")
|
|
self.tabs.addTab(self._create_updates_tab(), "Updates")
|
|
|
|
layout.addWidget(self.tabs)
|
|
|
|
def _create_general_tab(self):
|
|
"""Create general settings tab."""
|
|
tab = QWidget()
|
|
layout = QVBoxLayout(tab)
|
|
layout.setSpacing(16)
|
|
|
|
# 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"])
|
|
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)
|
|
opacity_layout.addWidget(self.opacity_slider)
|
|
self.opacity_label = QLabel("90%")
|
|
opacity_layout.addWidget(self.opacity_label)
|
|
opacity_layout.addStretch()
|
|
appear_layout.addLayout(opacity_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")
|
|
behavior_layout.addWidget(self.auto_start_cb)
|
|
|
|
self.minimize_cb = QCheckBox("Minimize to tray on close")
|
|
behavior_layout.addWidget(self.minimize_cb)
|
|
|
|
# Activity Bar Overlay Mode
|
|
overlay_group = QGroupBox("Activity Bar Overlay Mode")
|
|
overlay_group.setStyleSheet(self._group_style())
|
|
overlay_layout = QVBoxLayout(overlay_group)
|
|
|
|
overlay_info = QLabel("Choose how the in-game activity bar behaves:")
|
|
overlay_info.setStyleSheet("color: rgba(255,255,255,150); font-size: 12px;")
|
|
overlay_layout.addWidget(overlay_info)
|
|
|
|
self.overlay_mode_combo = QComboBox()
|
|
self.overlay_mode_combo.addItems([
|
|
"Game Focused Only (Blish HUD style)",
|
|
"Always Visible",
|
|
"Hotkey Toggle",
|
|
"Temporary (8 seconds)",
|
|
"Desktop App Only"
|
|
])
|
|
self.overlay_mode_combo.setCurrentIndex(0) # Default to game focused
|
|
self.overlay_mode_combo.currentIndexChanged.connect(self._on_overlay_mode_changed)
|
|
overlay_layout.addWidget(self.overlay_mode_combo)
|
|
|
|
overlay_desc = QLabel(
|
|
"Game Focused: Activity bar only appears when EU window is active. "
|
|
"Hidden on desktop."
|
|
)
|
|
overlay_desc.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;")
|
|
overlay_desc.setWordWrap(True)
|
|
overlay_layout.addWidget(overlay_desc)
|
|
|
|
behavior_layout.addWidget(overlay_group)
|
|
|
|
layout.addWidget(behavior_group)
|
|
layout.addStretch()
|
|
|
|
return tab
|
|
|
|
def _create_plugin_store_tab(self):
|
|
"""Create plugin store tab."""
|
|
from core.plugin_store import PluginStoreUI
|
|
|
|
if self.plugin_manager:
|
|
return PluginStoreUI(self.plugin_manager)
|
|
|
|
# Error fallback
|
|
tab = QWidget()
|
|
layout = QVBoxLayout(tab)
|
|
error = QLabel("Plugin Manager not available")
|
|
error.setStyleSheet("color: #ff4757;")
|
|
layout.addWidget(error)
|
|
return tab
|
|
|
|
def _create_plugins_tab(self):
|
|
"""Create installed plugins management tab."""
|
|
tab = QWidget()
|
|
layout = QVBoxLayout(tab)
|
|
|
|
info = QLabel("Manage your installed plugins. Enable/disable as needed.")
|
|
info.setStyleSheet("color: rgba(255,255,255,150);")
|
|
layout.addWidget(info)
|
|
|
|
# Plugin list will be populated here
|
|
scroll = QScrollArea()
|
|
scroll.setWidgetResizable(True)
|
|
scroll.setStyleSheet("background: transparent; border: none;")
|
|
|
|
self.plugins_container = QWidget()
|
|
self.plugins_layout = QVBoxLayout(self.plugins_container)
|
|
self.plugins_layout.setAlignment(Qt.AlignmentFlag.AlignTop)
|
|
|
|
self._populate_plugins_list()
|
|
|
|
scroll.setWidget(self.plugins_container)
|
|
layout.addWidget(scroll)
|
|
|
|
return tab
|
|
|
|
def _populate_plugins_list(self):
|
|
"""Populate the plugins list."""
|
|
if not self.plugin_manager:
|
|
return
|
|
|
|
# Clear existing
|
|
while self.plugins_layout.count():
|
|
item = self.plugins_layout.takeAt(0)
|
|
if item.widget():
|
|
item.widget().deleteLater()
|
|
|
|
all_plugins = self.plugin_manager.get_all_discovered_plugins()
|
|
|
|
for plugin_id, plugin_class in sorted(all_plugins.items(), key=lambda x: x[1].name):
|
|
row = QHBoxLayout()
|
|
|
|
# Checkbox
|
|
cb = QCheckBox(plugin_class.name)
|
|
cb.setChecked(self.plugin_manager.is_plugin_enabled(plugin_id))
|
|
cb.stateChanged.connect(lambda state, pid=plugin_id: self._toggle_plugin(pid, state))
|
|
row.addWidget(cb)
|
|
|
|
# Version
|
|
version = QLabel(f"v{plugin_class.version}")
|
|
version.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;")
|
|
row.addWidget(version)
|
|
|
|
# Description
|
|
desc = QLabel(plugin_class.description)
|
|
desc.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;")
|
|
desc.setWordWrap(True)
|
|
row.addWidget(desc, 1)
|
|
|
|
self.plugins_layout.addLayout(row)
|
|
|
|
# Separator
|
|
sep = QFrame()
|
|
sep.setFrameShape(QFrame.Shape.HLine)
|
|
sep.setStyleSheet("background-color: rgba(255, 140, 66, 0.1);")
|
|
sep.setFixedHeight(1)
|
|
self.plugins_layout.addWidget(sep)
|
|
|
|
self.plugins_layout.addStretch()
|
|
|
|
def _toggle_plugin(self, plugin_id: str, state: int):
|
|
"""Enable or disable a plugin."""
|
|
if not self.plugin_manager:
|
|
return
|
|
|
|
if state == Qt.CheckState.Checked.value:
|
|
self.plugin_manager.enable_plugin(plugin_id)
|
|
else:
|
|
self.plugin_manager.disable_plugin(plugin_id)
|
|
|
|
def _create_hotkeys_tab(self):
|
|
"""Create hotkeys configuration tab."""
|
|
tab = QWidget()
|
|
layout = QVBoxLayout(tab)
|
|
layout.setSpacing(16)
|
|
|
|
info = QLabel("Configure keyboard shortcuts. Changes apply on restart.")
|
|
info.setStyleSheet("color: rgba(255,255,255,150);")
|
|
layout.addWidget(info)
|
|
|
|
# Core hotkeys
|
|
group = QGroupBox("Core Hotkeys")
|
|
group.setStyleSheet(self._group_style())
|
|
group_layout = QVBoxLayout(group)
|
|
|
|
hotkeys = [
|
|
("Toggle Overlay", "ctrl+shift+u"),
|
|
("Quick Search", "ctrl+shift+f"),
|
|
("Settings", "ctrl+shift+comma"),
|
|
]
|
|
|
|
for label, default in hotkeys:
|
|
row = QHBoxLayout()
|
|
row.addWidget(QLabel(f"{label}:"))
|
|
|
|
input_field = QLineEdit(default)
|
|
input_field.setStyleSheet("""
|
|
QLineEdit {
|
|
background-color: rgba(20, 31, 35, 0.95);
|
|
color: white;
|
|
border: 1px solid rgba(255, 140, 66, 0.2);
|
|
padding: 5px;
|
|
min-width: 150px;
|
|
}
|
|
""")
|
|
row.addWidget(input_field)
|
|
row.addStretch()
|
|
group_layout.addLayout(row)
|
|
|
|
layout.addWidget(group)
|
|
layout.addStretch()
|
|
|
|
return tab
|
|
|
|
def _create_data_tab(self):
|
|
"""Create data and backup tab."""
|
|
tab = QWidget()
|
|
layout = QVBoxLayout(tab)
|
|
layout.setSpacing(16)
|
|
|
|
# Backup section
|
|
backup_group = QGroupBox("Backup & Restore")
|
|
backup_group.setStyleSheet(self._group_style())
|
|
backup_layout = QVBoxLayout(backup_group)
|
|
|
|
export_btn = QPushButton("Export All Data")
|
|
export_btn.setStyleSheet(self._button_style("#ff8c42"))
|
|
export_btn.clicked.connect(self._export_data)
|
|
backup_layout.addWidget(export_btn)
|
|
|
|
import_btn = QPushButton("Import Data")
|
|
import_btn.setStyleSheet(self._button_style("#4ecdc4"))
|
|
import_btn.clicked.connect(self._import_data)
|
|
backup_layout.addWidget(import_btn)
|
|
|
|
layout.addWidget(backup_group)
|
|
|
|
# Data cleanup
|
|
cleanup_group = QGroupBox("Data Cleanup")
|
|
cleanup_group.setStyleSheet(self._group_style())
|
|
cleanup_layout = QVBoxLayout(cleanup_group)
|
|
|
|
clear_btn = QPushButton("Clear All Data")
|
|
clear_btn.setStyleSheet(self._button_style("#ff4757"))
|
|
clear_btn.clicked.connect(self._clear_data)
|
|
cleanup_layout.addWidget(clear_btn)
|
|
|
|
layout.addWidget(cleanup_group)
|
|
layout.addStretch()
|
|
|
|
return tab
|
|
|
|
def _create_updates_tab(self):
|
|
"""Create auto-updater settings tab."""
|
|
tab = QWidget()
|
|
layout = QVBoxLayout(tab)
|
|
layout.setSpacing(16)
|
|
|
|
# Auto-update settings
|
|
update_group = QGroupBox("Automatic Updates")
|
|
update_group.setStyleSheet(self._group_style())
|
|
update_layout = QVBoxLayout(update_group)
|
|
|
|
self.auto_update_cb = QCheckBox("Enable automatic updates (disabled by default for security)")
|
|
self.auto_update_cb.setChecked(False)
|
|
update_layout.addWidget(self.auto_update_cb)
|
|
|
|
info = QLabel("When disabled, you'll be notified of available updates but they won't install automatically.")
|
|
info.setStyleSheet("color: rgba(255,255,255,100); font-size: 11px;")
|
|
info.setWordWrap(True)
|
|
update_layout.addWidget(info)
|
|
|
|
layout.addWidget(update_group)
|
|
|
|
# Manual check
|
|
check_btn = QPushButton("Check for Updates Now")
|
|
check_btn.setStyleSheet(self._button_style("#ff8c42"))
|
|
check_btn.clicked.connect(self._check_updates)
|
|
layout.addWidget(check_btn)
|
|
|
|
layout.addStretch()
|
|
|
|
return tab
|
|
|
|
def _on_overlay_mode_changed(self, index: int):
|
|
"""Handle overlay mode change."""
|
|
mode_map = {
|
|
0: "overlay_game", # Game Focused Only
|
|
1: "overlay_always", # Always Visible
|
|
2: "overlay_toggle", # Hotkey Toggle
|
|
3: "overlay_temp", # Temporary
|
|
4: "desktop_app" # Desktop App Only
|
|
}
|
|
|
|
mode = mode_map.get(index, "overlay_game")
|
|
|
|
# Save to settings
|
|
if hasattr(self.overlay, 'settings'):
|
|
self.overlay.settings['activity_bar.overlay_mode'] = mode
|
|
self.overlay.settings.save()
|
|
|
|
print(f"[Settings] Activity Bar mode changed to: {mode}")
|
|
|
|
# Show confirmation
|
|
QMessageBox.information(
|
|
self,
|
|
"Mode Changed",
|
|
f"Activity Bar mode changed to: {self.overlay_mode_combo.currentText()}\n\n"
|
|
"Restart EU-Utility for changes to take effect."
|
|
)
|
|
|
|
def _export_data(self):
|
|
"""Export all data."""
|
|
path, _ = QFileDialog.getSaveFileName(
|
|
self, "Export Data", "eu_utility_backup.json", "JSON (*.json)"
|
|
)
|
|
if path:
|
|
QMessageBox.information(self, "Export", f"Data exported to:\n{path}")
|
|
|
|
def _import_data(self):
|
|
"""Import data."""
|
|
path, _ = QFileDialog.getOpenFileName(
|
|
self, "Import Data", "", "JSON (*.json)"
|
|
)
|
|
if path:
|
|
QMessageBox.information(self, "Import", "Data imported successfully!")
|
|
|
|
def _clear_data(self):
|
|
"""Clear all data."""
|
|
reply = QMessageBox.question(
|
|
self, "Confirm",
|
|
"Are you sure you want to clear all data?\n\nThis cannot be undone!",
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
)
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|
QMessageBox.information(self, "Cleared", "All data has been cleared.")
|
|
|
|
def _check_updates(self):
|
|
"""Check for updates."""
|
|
QMessageBox.information(self, "Updates", "You are running the latest version!")
|
|
|
|
def _group_style(self):
|
|
"""Get group box style."""
|
|
return """
|
|
QGroupBox {
|
|
color: rgba(255,255,255,200);
|
|
border: 1px solid rgba(255, 140, 66, 0.2);
|
|
border-radius: 6px;
|
|
margin-top: 10px;
|
|
font-weight: bold;
|
|
font-size: 12px;
|
|
}
|
|
QGroupBox::title {
|
|
subcontrol-origin: margin;
|
|
left: 10px;
|
|
padding: 0 5px;
|
|
}
|
|
"""
|
|
|
|
def _button_style(self, color):
|
|
"""Get button style with color."""
|
|
return f"""
|
|
QPushButton {{
|
|
background-color: {color};
|
|
color: white;
|
|
padding: 10px 20px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
font-weight: bold;
|
|
}}
|
|
QPushButton:hover {{
|
|
background-color: {color}dd;
|
|
}}
|
|
"""
|