# Settings and Configuration CppLab IDE provides user-configurable settings for customizing appearance, build behavior, and development preferences. ## Settings Storage ### Location **User Settings File**: ``` Windows: C:\Users\\.cpplab\settings.json Linux: ~/.cpplab/settings.json macOS: ~/.cpplab/settings.json ``` **Format**: JSON **Example**: ```json { "theme": "classic", "font_size": 10, "bold_font": false, "auto_save": true, "show_line_numbers": true, "tab_width": 4, "default_standard_c": "c17", "default_standard_cpp": "c++17", "default_toolchain": "mingw64", "build_before_run": true, "show_build_time": true } ``` ### Persistence **When Settings Are Saved**: - [x] On settings dialog "OK" button - [x] On application exit (window position/size) - [x] On theme change - [x] On font change **When Settings Are Loaded**: - [x] On application startup - [x] On settings dialog open - [x] After settings change ## Settings Dialog ### Access **Method 1: Menu** ``` Tools → Settings... (Alt+S) ``` **Method 2: Toolbar** ``` Click ⚙ (Settings) icon ``` **Method 3: Keyboard** ``` Ctrl+Comma ``` ### Dialog Structure ``` ┌─────────────────────────────────────────┐ │ Settings [X] │ ├─────────────────────────────────────────┤ │ ┌─────────────────────────────────────┐ │ │ │ [Appearance] [Build] [Editor] [Advanced] │ │ └─────────────────────────────────────┘ │ │ │ │ Theme: [Classic ▼] │ │ Font Size: [10 ▼] │ │ ☑ Bold Font │ │ ☑ Show Line Numbers │ │ │ │ [OK] [Cancel] [Apply] │ └─────────────────────────────────────────┘ ``` ## Settings Categories ### Appearance **Theme**: ``` Options: Classic, Sky Blue, Dark (future) Default: Classic Description: Overall color scheme ``` **Font Settings**: ``` Font Size: 8, 9, 10, 11, 12, 14, 16, 18, 20 Default: 10 Description: Size of text in editor and output panels Font Family: Consolas (fixed) Description: Monospace font for code Bold Font: Checkbox Default: Unchecked Description: Make all text bold ``` **UI Scale** (future): ``` Options: 100%, 125%, 150%, 200% Default: 100% Description: Scale entire UI ``` ### Build **Default Standards**: ``` C Standard: C99, C11, C17, C18, C23 Default: C17 Description: Default for new C projects C++ Standard: C++11, C++14, C++17, C++20, C++23 Default: C++17 Description: Default for new C++ projects ``` **Default Toolchain**: ``` Options: mingw32, mingw64 Default: mingw64 Description: Toolchain for new projects ``` **Build Behavior**: ``` ☑ Build Before Run Default: Checked Description: Automatically build before running ☑ Show Build Time Default: Checked Description: Display elapsed time in status bar ☑ Save All Before Build Default: Checked Description: Save all open files before building ☐ Verbose Build Output Default: Unchecked Description: Show detailed compiler output ``` **Optimization Level**: ``` Options: -O0 (None), -O1 (Basic), -O2 (Moderate), -O3 (Aggressive) Default: -O0 Description: Compiler optimization level ``` ### Editor **Indentation**: ``` Tab Width: 2, 4, 8 Default: 4 Description: Number of spaces per tab ☐ Use Spaces Instead of Tabs Default: Unchecked Description: Insert spaces when Tab key pressed ``` **Display**: ``` ☑ Show Line Numbers Default: Checked Description: Show line numbers in gutter ☐ Show Whitespace Default: Unchecked Description: Show spaces and tabs ☑ Highlight Current Line Default: Checked Description: Highlight line with cursor ``` **Behavior**: ``` ☑ Auto-Indent Default: Checked Description: Auto-indent on newline ☑ Auto-Complete Brackets Default: Checked Description: Auto-close {}, [], (), "" ☑ Word Wrap Default: Unchecked Description: Wrap long lines ``` ### Advanced **Paths**: ``` Toolchain Directory: [Browse...] Default: compilers/ (relative to exe) Description: Location of MinGW toolchains Project Templates Dir: [Browse...] Default: templates/ (relative to exe) Description: Custom project templates ``` **Performance**: ``` Max Undo History: 100, 500, 1000, Unlimited Default: 1000 Description: Number of undo steps Max File Size: 1 MB, 5 MB, 10 MB, 50 MB Default: 10 MB Description: Max file size to open ``` **Debugging**: ``` ☐ Enable Debug Logging Default: Unchecked Description: Write logs to cpplab.log ☐ Show Internal Errors Default: Unchecked Description: Show Python tracebacks ``` ## Implementation ### Data Model **File**: `src/cpplab/settings.py` ```python from dataclasses import dataclass, asdict from pathlib import Path import json @dataclass class AppSettings: """Application settings.""" # Appearance theme: str = "classic" font_size: int = 10 bold_font: bool = False # Build default_standard_c: str = "c17" default_standard_cpp: str = "c++17" default_toolchain: str = "mingw64" build_before_run: bool = True show_build_time: bool = True optimization_level: str = "-O0" # Editor tab_width: int = 4 use_spaces: bool = False show_line_numbers: bool = True auto_indent: bool = True # Advanced max_undo_history: int = 1000 max_file_size_mb: int = 10 debug_logging: bool = False def get_settings_path() -> Path: """Get path to settings file.""" settings_dir = Path.home() / ".cpplab" settings_dir.mkdir(exist_ok=True) return settings_dir / "settings.json" def load_settings() -> AppSettings: """Load settings from JSON file.""" settings_path = get_settings_path() if not settings_path.exists(): # Return defaults return AppSettings() try: with open(settings_path, 'r', encoding='utf-8') as f: data = json.load(f) return AppSettings(**data) except Exception as e: print(f"Error loading settings: {e}") return AppSettings() def save_settings(settings: AppSettings): """Save settings to JSON file.""" settings_path = get_settings_path() try: with open(settings_path, 'w', encoding='utf-8') as f: json.dump(asdict(settings), f, indent=2) except Exception as e: print(f"Error saving settings: {e}") ``` ### Settings Dialog **File**: `src/cpplab/settings_dialog.py` ```python from PyQt6.QtWidgets import ( QDialog, QTabWidget, QVBoxLayout, QHBoxLayout, QLabel, QComboBox, QCheckBox, QPushButton, QGroupBox, QSpinBox ) from PyQt6.QtCore import Qt class SettingsDialog(QDialog): """Settings dialog with tabbed interface.""" def __init__(self, current_settings: AppSettings, parent=None): super().__init__(parent) self.settings = current_settings self.setWindowTitle("Settings") self.resize(500, 400) # Create layout layout = QVBoxLayout() # Create tabs tabs = QTabWidget() tabs.addTab(self._create_appearance_tab(), "Appearance") tabs.addTab(self._create_build_tab(), "Build") tabs.addTab(self._create_editor_tab(), "Editor") layout.addWidget(tabs) # Buttons button_layout = QHBoxLayout() button_layout.addStretch() ok_button = QPushButton("OK") ok_button.clicked.connect(self.accept) cancel_button = QPushButton("Cancel") cancel_button.clicked.connect(self.reject) apply_button = QPushButton("Apply") apply_button.clicked.connect(self.apply_settings) button_layout.addWidget(ok_button) button_layout.addWidget(cancel_button) button_layout.addWidget(apply_button) layout.addLayout(button_layout) self.setLayout(layout) def _create_appearance_tab(self): """Create appearance settings tab.""" widget = QWidget() layout = QVBoxLayout() # Theme theme_group = QGroupBox("Theme") theme_layout = QVBoxLayout() self.theme_combo = QComboBox() self.theme_combo.addItems(["classic", "sky_blue"]) self.theme_combo.setCurrentText(self.settings.theme) theme_layout.addWidget(QLabel("Theme:")) theme_layout.addWidget(self.theme_combo) theme_group.setLayout(theme_layout) # Font font_group = QGroupBox("Font") font_layout = QVBoxLayout() self.font_size_spin = QSpinBox() self.font_size_spin.setRange(8, 20) self.font_size_spin.setValue(self.settings.font_size) self.bold_check = QCheckBox("Bold Font") self.bold_check.setChecked(self.settings.bold_font) font_layout.addWidget(QLabel("Font Size:")) font_layout.addWidget(self.font_size_spin) font_layout.addWidget(self.bold_check) font_group.setLayout(font_layout) layout.addWidget(theme_group) layout.addWidget(font_group) layout.addStretch() widget.setLayout(layout) return widget def _create_build_tab(self): """Create build settings tab.""" # Similar structure... pass def apply_settings(self): """Apply current settings.""" # Update settings object self.settings.theme = self.theme_combo.currentText() self.settings.font_size = self.font_size_spin.value() self.settings.bold_font = self.bold_check.isChecked() # Save to disk save_settings(self.settings) # Emit signal or call parent method if self.parent(): self.parent().apply_settings() def get_settings(self) -> AppSettings: """Get modified settings.""" return self.settings ``` ### Applying Settings **File**: `src/cpplab/app.py` ```python class MainWindow(QMainWindow): def __init__(self): super().__init__() # Load settings on startup self.settings = load_settings() self.apply_settings() def apply_settings(self): """Apply current settings to UI.""" # Theme if self.settings.theme in THEMES: self.setStyleSheet(THEMES[self.settings.theme]) # Font font = QFont("Consolas", self.settings.font_size) if self.settings.bold_font: font.setBold(True) self.editor.setFont(font) self.buildOutputEdit.setFont(font) self.problemsTextEdit.setFont(font) self.consoleTextEdit.setFont(font) # Editor settings tab_width = self.settings.tab_width * 10 # Pixels self.editor.setTabStopDistance(tab_width) # Show/hide line numbers if self.settings.show_line_numbers: # Enable line numbers pass def show_settings_dialog(self): """Show settings dialog.""" dialog = SettingsDialog(self.settings, self) if dialog.exec() == QDialog.DialogCode.Accepted: # Get updated settings self.settings = dialog.get_settings() save_settings(self.settings) # Apply changes self.apply_settings() ``` ## Themes ### Built-in Themes **Classic Theme**: ```python THEMES = { "classic": """ QMainWindow { background-color: #f0f0f0; } QMenuBar { background-color: #ffffff; border-bottom: 1px solid #c0c0c0; } QTextEdit { background-color: #ffffff; color: #000000; border: 1px solid #c0c0c0; } QPushButton { background-color: #e1e1e1; color: #000000; border: 1px solid #adadad; padding: 5px 10px; border-radius: 3px; } QPushButton:hover { background-color: #d0d0d0; } QStatusBar { background-color: #007acc; color: #ffffff; } """ } ``` **Sky Blue Theme**: ```python THEMES["sky_blue"] = """ QMainWindow { background-color: #e6f2ff; } QMenuBar { background-color: #cce0ff; border-bottom: 1px solid #99c2ff; } QTextEdit { background-color: #f0f8ff; color: #003366; border: 1px solid #99c2ff; } QPushButton { background-color: #4a90d9; color: #ffffff; border: none; padding: 5px 10px; border-radius: 3px; } QPushButton:hover { background-color: #5fa3e8; } QStatusBar { background-color: #2a5a8a; color: #ffffff; } """ ``` ### Custom Themes (Future) **User-Defined Themes**: ```json { "theme": "custom", "custom_theme_path": "C:/Users/User/.cpplab/themes/my_theme.qss" } ``` **Theme File** (`my_theme.qss`): ```css QMainWindow { background-color: #282c34; } QTextEdit { background-color: #1e2127; color: #abb2bf; } ``` ## Project Settings ### Location **Project Settings File**: ``` /cpplab_project.json ``` **Example**: ```json { "name": "MyApp", "language": "cpp", "standard": "c++17", "project_type": "console", "toolchain": "mingw64", "compile_flags": ["-Wall", "-Wextra"], "link_flags": [], "sources": ["src/main.cpp"] } ``` ### Project vs User Settings **User Settings** (global): - Theme - Font - Default toolchain - Default standards - Editor preferences **Project Settings** (per-project): - Project name - Language - Standard (override user default) - Toolchain (override user default) - Compile/link flags - Source files **Priority**: Project settings override user settings ## Reset to Defaults ### Method 1: Settings Dialog ``` Settings → Advanced → [Reset to Defaults] button ``` ### Method 2: Delete Settings File **Windows**: ```powershell del C:\Users\\.cpplab\settings.json ``` **Linux/macOS**: ```bash rm ~/.cpplab/settings.json ``` **On Next Launch**: Default settings used ### Method 3: Code ```python # Reset to defaults settings = AppSettings() save_settings(settings) ``` ## Migration ### Settings Version **Future-Proofing**: ```json { "version": 1, "theme": "classic", ... } ``` ### Upgrade Path ```python def load_settings() -> AppSettings: """Load settings with version migration.""" settings_path = get_settings_path() if not settings_path.exists(): return AppSettings() with open(settings_path, 'r') as f: data = json.load(f) # Check version version = data.get("version", 0) if version == 0: # Migrate from v0 to v1 data = migrate_v0_to_v1(data) return AppSettings(**data) ``` --- **Next**: [Performance and Benchmarks](Performance-And-Benchmarks.md) **Previous**: [UI Framework and PyQt6](UI-Framework-And-PyQt6.md)