rusty_emu/src/gui/components.rs

804 lines
31 KiB
Rust

//! # GUI Components Module
//!
//! This module implements all the individual GUI components for the Intel MCS-4 emulator.
//! Each component is responsible for rendering its specific section of the interface
//! and handling user interactions within that section.
//!
//! ## Component Architecture
//!
//! The GUI is organized into several specialized components:
//!
//! - **`GuiComponents`**: Main container managing all components
//! - **`ControlPanel`**: System control buttons and actions
//! - **`MemoryViewer`**: RAM content display and inspection
//! - **`RegisterViewer`**: CPU register state visualization
//! - **`RomLoader`**: File dialog integration for ROM loading
//! - **`StatusBar`**: System status and component health display
//!
//! ## Design Principles
//!
//! ### Separation of Concerns
//! Each component handles only its specific functionality:
//! - State management is centralized in `GuiState`
//! - Components receive immutable references where possible
//! - User interactions trigger state changes through controlled interfaces
//!
//! ### Performance Optimization
//! - Components minimize allocations and cloning
//! - State updates are batched and efficient
//! - UI layout uses egui's immediate mode for optimal performance
//!
//! ### User Experience
//! - Consistent visual design across all components
//! - Clear visual feedback for user actions
//! - Intuitive layout and component organization
//!
//! ## Component Communication
//!
//! Components communicate through the shared `GuiState`:
//! - Read-only access to state for display components
//! - Controlled mutation through specific methods
//! - Thread-safe state sharing with the emulator system
//!
//! ## Error Handling
//!
//! Components handle errors gracefully:
//! - Invalid states are displayed to users
//! - Failed operations show clear error messages
//! - Recovery options are provided where applicable
use super::state::GuiState;
use eframe::egui;
/// Container for all GUI components
///
/// This structure manages the lifecycle and coordination of all GUI components.
/// It acts as a facade, providing a clean interface for rendering different
/// sections of the user interface while maintaining proper separation of concerns.
///
/// ## Component Management
///
/// The container is responsible for:
/// - Component initialization and configuration
/// - Render orchestration and layout management
/// - State distribution to appropriate components
/// - Component lifecycle and cleanup
///
/// ## Performance Considerations
///
/// - Components are created once and reused across frames
/// - State references are passed efficiently without cloning
/// - Layout is managed centrally for consistent spacing
pub struct GuiComponents {
/// System control and management interface
control_panel: ControlPanel,
/// RAM content inspection and visualization
memory_viewer: MemoryViewer,
/// CPU register state display
register_viewer: RegisterViewer,
/// ROM file loading and management
#[allow(dead_code)] // RomLoader is currently a placeholder implementation
rom_loader: RomLoader,
/// System status and health monitoring
status_bar: StatusBar,
}
impl GuiComponents {
/// Create a new GUI components container with all components initialized
///
/// This constructor sets up all GUI components with their default configurations.
/// Components are ready to use immediately after creation.
///
/// # Returns
/// A fully initialized `GuiComponents` instance
///
/// # Example
/// ```rust,no_run
/// use rusty_emu::gui::components::GuiComponents;
///
/// let mut components = GuiComponents::new();
/// // Components are ready to render
/// ```
pub fn new() -> Self {
Self {
control_panel: ControlPanel::new(),
memory_viewer: MemoryViewer::new(),
register_viewer: RegisterViewer::new(),
rom_loader: RomLoader::new(),
status_bar: StatusBar::new(),
}
}
/// Render the control panel component
///
/// The control panel provides system management functionality including
/// start/stop/reset controls and ROM loading capabilities.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Mutable reference to GUI state for control operations
pub fn render_control_panel(&mut self, ui: &mut egui::Ui, state: &mut GuiState) {
self.control_panel.render(ui, state);
}
/// Render the memory viewer component
///
/// Displays RAM contents in a tabular format with bank selection
/// and hex/decimal viewing options.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state for display
pub fn render_memory_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
self.memory_viewer.render(ui, state);
}
/// Render the register viewer component
///
/// Shows CPU register state including accumulator, program counter,
/// index registers, and system flags.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state for display
pub fn render_register_viewer(&self, ui: &mut egui::Ui, state: &GuiState) {
self.register_viewer.render(ui, state);
}
/// Render the status bar component
///
/// Displays system health, component status, cycle counts,
/// and any error conditions.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state for display
pub fn render_status_bar(&self, ui: &mut egui::Ui, state: &GuiState) {
self.status_bar.render(ui, state);
}
}
/// Control panel component for system control buttons and operations
///
/// The control panel provides the primary interface for managing the emulator system.
/// It offers intuitive controls for starting, stopping, and resetting the emulation,
/// along with system configuration and file management capabilities.
///
/// ## Features
///
/// - **System Lifecycle Management**: Start, stop, and reset emulator execution
/// - **Visual State Feedback**: Dynamic button states reflecting current system status
/// - **Error Handling**: Clear error display and recovery mechanisms
/// - **Application Control**: Window management and application termination
///
/// ## Button States
///
/// The control panel adapts its interface based on system state:
/// - **Stopped**: Shows "Start System" button, disabled stop/reset buttons
/// - **Running**: Shows "System Running..." text, enabled stop/reset buttons
/// - **Error**: Displays error messages with option to clear them
pub struct ControlPanel {
/// Dynamic text for the start/stop button based on system state
start_button_text: String,
/// Whether the stop button should be enabled
stop_button_enabled: bool,
/// Whether the reset button should be enabled
reset_button_enabled: bool,
}
impl ControlPanel {
/// Create a new control panel with default state
///
/// Initializes the control panel in the "stopped" state with appropriate
/// button configurations for a system that's ready to start.
///
/// # Returns
/// A new `ControlPanel` instance ready for system control
pub fn new() -> Self {
Self {
start_button_text: "Start System".to_string(),
stop_button_enabled: false,
reset_button_enabled: false,
}
}
/// Render the control panel interface
///
/// Creates a horizontal layout with all control buttons and handles
/// user interactions. The interface adapts dynamically based on system state.
///
/// # Arguments
/// * `ui` - egui UI context for rendering and interaction
/// * `state` - Mutable reference to GUI state for system control
///
/// # Layout
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ System Control ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ │
/// │ [Load ROM] [Start System] [Stop] [Reset] [Close] │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&mut self, ui: &mut egui::Ui, state: &mut GuiState) {
ui.horizontal(|ui| {
// Section header
ui.heading("System Control");
// ROM loading button
if ui.button("Load ROM").clicked() {
// ROM loading will be handled by RomLoader component
state.set_error("ROM loader not yet implemented".to_string());
}
// Start/Stop system button (context-sensitive)
if ui.button(&self.start_button_text).clicked() {
if !state.system_running {
self.start_system(state);
}
}
// Stop system button (only enabled when running)
if ui.button("Stop System").clicked() && state.system_running {
self.stop_system(state);
}
// Reset system button
if ui.button("Reset System").clicked() {
self.reset_system(state);
}
// Application close button
if ui.button("Close").clicked() {
// Close application - handled by eframe
ui.ctx().send_viewport_cmd(egui::ViewportCommand::Close);
}
});
ui.separator();
}
/// Start the emulator system
///
/// Transitions the system from stopped to running state and updates
/// all related UI elements to reflect the new state.
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
fn start_system(&mut self, state: &mut GuiState) {
state.system_running = true;
self.start_button_text = "System Running...".to_string();
self.stop_button_enabled = true;
self.reset_button_enabled = true;
state.clear_error();
}
/// Stop the emulator system
///
/// Transitions the system from running to stopped state and updates
/// UI elements accordingly.
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
fn stop_system(&mut self, state: &mut GuiState) {
state.system_running = false;
self.start_button_text = "Start System".to_string();
self.stop_button_enabled = false;
state.clear_error();
}
/// Reset the emulator system
///
/// Performs a complete system reset, stopping execution and clearing
/// all state including cycle counts and error conditions.
///
/// # Arguments
/// * `state` - Mutable reference to GUI state
fn reset_system(&mut self, state: &mut GuiState) {
state.system_running = false;
state.cycle_count = 0;
self.start_button_text = "Start System".to_string();
self.stop_button_enabled = false;
self.reset_button_enabled = false;
state.clear_error();
}
}
/// Memory viewer component for displaying RAM contents and state
///
/// The memory viewer provides comprehensive RAM inspection capabilities,
/// allowing users to examine memory contents across different banks with
/// flexible display options and intuitive navigation.
///
/// ## Features
///
/// - **Multi-Bank Display**: View RAM contents across 4 banks simultaneously
/// - **Flexible Formatting**: Toggle between hexadecimal and decimal display
/// - **Interactive Bank Selection**: Choose which memory bank to inspect
/// - **Scrollable Interface**: Navigate through memory contents efficiently
/// - **Real-time Updates**: Live memory content updates during emulation
///
/// ## Display Format
///
/// The memory viewer shows data in a structured grid:
/// - **Address Column**: Memory addresses in hexadecimal format
/// - **Bank Columns**: B0-B3 showing contents of each memory bank
/// - **Value Display**: Configurable hex or decimal representation
///
/// ## Memory Organization
///
/// The Intel 4002 RAM has the following structure:
/// - **4 Banks**: Independent memory banks (B0-B3)
/// - **4 Bytes per Bank**: Addresses 0x00-0x03 in each bank
/// - **4-bit Values**: Each memory location stores a 4-bit nibble
pub struct MemoryViewer {
/// Display mode: true for hexadecimal, false for decimal
show_hex: bool,
/// Number of bytes to display per row (currently fixed at 16)
#[allow(dead_code)]
// bytes_per_row is currently fixed at 16 but kept for future flexibility
bytes_per_row: usize,
}
impl MemoryViewer {
/// Create a new memory viewer with default configuration
///
/// Initializes the viewer with hexadecimal display mode and standard
/// layout optimized for the Intel 4002 RAM structure.
///
/// # Returns
/// A new `MemoryViewer` instance ready for RAM content display
pub fn new() -> Self {
Self {
show_hex: true,
bytes_per_row: 16,
}
}
/// Render the memory viewer interface
///
/// Creates a comprehensive memory inspection interface with bank selection,
/// display options, and a scrollable memory contents grid.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state containing memory data
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ Memory Viewer [─] [□] │
/// │ Bank: [0] □ Hex View │
/// ├─────────────────────────────────────────────────┤
/// │ Address B0 B1 B2 B3 │
/// │ [00] [12] [34] [56] [78] │
/// │ [01] [9A] [BC] [DE] [F0] │
/// │ [02] [11] [22] [33] [44] │
/// │ [03] [55] [66] [77] [88] │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
ui.vertical(|ui| {
// Section header
ui.heading("Memory Viewer");
// Control bar with bank selection and display options
ui.horizontal(|ui| {
ui.label("Bank:");
// Bank selector (0-3 for Intel 4002)
ui.add(
egui::DragValue::new(&mut state.memory_state.selected_bank.clone())
.clamp_range(0..=3),
);
ui.checkbox(&mut self.show_hex.clone(), "Hex View");
});
ui.separator();
// Memory contents in scrollable area
egui::ScrollArea::vertical().show(ui, |ui| {
egui::Grid::new("memory_grid").striped(true).show(ui, |ui| {
// Header row with bank labels
ui.label("Address");
for i in 0..4 {
ui.label(format!("B{}", i));
}
ui.end_row();
// Memory contents rows
for addr in 0..4 {
// Address column
ui.label(format!("{:02X}", addr));
// Bank data columns
for bank in 0..4 {
let value = state.memory_state.ram_contents[bank][addr];
if self.show_hex {
ui.label(format!("{:02X}", value));
} else {
ui.label(format!("{}", value));
}
}
ui.end_row();
}
});
});
});
ui.separator();
}
}
/// Register viewer component for displaying CPU register state
///
/// The register viewer provides comprehensive CPU state visualization,
/// showing all key registers, flags, and pointers that define the current
/// execution state of the Intel 4004 microprocessor.
///
/// ## Features
///
/// - **Complete Register Set**: Display all 16 index registers (R0-R15)
/// - **Core CPU State**: Accumulator, program counter, stack pointer
/// - **Flag Visualization**: Carry flag status with clear indicators
/// - **Interactive Selection**: Choose which index register to highlight
/// - **Dual Format Display**: Hexadecimal and decimal representations
/// - **Real-time Updates**: Live register state during emulation
///
/// ## Intel 4004 Register Architecture
///
/// The Intel 4004 has the following register structure:
/// - **Accumulator**: Main arithmetic register (4 bits)
/// - **Index Registers**: 16 general-purpose registers (R0-R15)
/// - **Program Counter**: 12-bit address pointer
/// - **Stack Pointer**: 4-bit stack address register
/// - **Carry Flag**: Arithmetic carry/borrow indicator
///
/// ## Display Organization
///
/// Registers are organized in a clear hierarchy:
/// - **Primary Registers**: Most frequently used (accumulator, PC, stack)
/// - **Flags**: System status indicators
/// - **Index Registers**: General-purpose register file
pub struct RegisterViewer {
/// Currently selected index register for detailed view (0-15)
selected_register: usize,
}
impl RegisterViewer {
/// Create a new register viewer with default configuration
///
/// Initializes the viewer with index register 0 selected and standard
/// display formatting optimized for the Intel 4004 architecture.
///
/// # Returns
/// A new `RegisterViewer` instance ready for CPU state display
pub fn new() -> Self {
Self {
selected_register: 0,
}
}
/// Render the register viewer interface
///
/// Creates a comprehensive register inspection interface with organized
/// display of all CPU state elements and interactive index register selection.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state containing register data
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ CPU Registers [─] [□] │
/// │ Index Register: [0] │
/// ├─────────────────────────────────────────────────┤
/// │ Register │ Value (Hex) │ Value (Dec) │
/// │ Accumulator │ 0F │ 15 │
/// │ Carry Flag │ 1 │ Set │
/// │ Program Counter│ 123 │ 291 │
/// │ Index R0 │ 2A │ 42 │
/// │ Stack Pointer │ 08 │ 8 │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
ui.vertical(|ui| {
// Section header
ui.heading("CPU Registers");
// Index register selector
ui.horizontal(|ui| {
ui.label("Index Register:");
ui.add(
egui::DragValue::new(&mut self.selected_register.clone()).clamp_range(0..=15),
);
});
ui.separator();
// Register display grid
egui::Grid::new("register_grid")
.striped(true)
.show(ui, |ui| {
// Header row
ui.label("Register");
ui.label("Value (Hex)");
ui.label("Value (Dec)");
ui.end_row();
// Core CPU registers
ui.label("Accumulator");
ui.label(format!("{:02X}", state.register_state.accumulator));
ui.label(format!("{}", state.register_state.accumulator));
ui.end_row();
// System flags
ui.label("Carry Flag");
ui.label(if state.register_state.carry_flag {
"1"
} else {
"0"
});
ui.label(if state.register_state.carry_flag {
"Set"
} else {
"Clear"
});
ui.end_row();
// Program execution state
ui.label("Program Counter");
ui.label(format!("{:03X}", state.register_state.program_counter));
ui.label(format!("{}", state.register_state.program_counter));
ui.end_row();
// Selected index register (highlighted)
ui.label(format!("Index R{}", self.selected_register));
ui.label(format!(
"{:02X}",
state.register_state.index_registers[self.selected_register]
));
ui.label(format!(
"{}",
state.register_state.index_registers[self.selected_register]
));
ui.end_row();
// Stack management
ui.label("Stack Pointer");
ui.label(format!("{:02X}", state.register_state.stack_pointer));
ui.label(format!("{}", state.register_state.stack_pointer));
ui.end_row();
});
});
ui.separator();
}
}
/// ROM loader component for file dialog integration and program management
///
/// The ROM loader handles program file selection, loading, and management.
/// It provides the interface for users to load Intel 4001 ROM files and
/// binary programs into the emulator system.
///
/// ## Features
///
/// - **File Dialog Integration**: Native file browser for program selection
/// - **Program Validation**: Basic file format and size validation
/// - **Load Feedback**: Visual confirmation of successful loads
/// - **Error Handling**: Clear error messages for failed operations
/// - **Future Extensions**: Support for multiple ROM chips and formats
///
/// ## Supported Formats
///
/// - **Binary Files**: Raw binary program data (.bin)
/// - **Intel 4001 Format**: MCS-4 ROM file format (planned)
/// - **Configuration Files**: System configuration integration (planned)
///
/// ## Integration Notes
///
/// Currently shows placeholder for file dialog implementation.
/// Production version should integrate with native file dialogs using:
/// - **rfd** crate for cross-platform file dialogs
/// - **async** operations for non-blocking file I/O
/// - **Progress feedback** for large file operations
pub struct RomLoader {
/// Whether to show the file selection dialog
show_file_dialog: bool,
/// Currently selected file path (if any)
selected_file: Option<String>,
}
impl RomLoader {
/// Create a new ROM loader with default state
///
/// Initializes the loader with no file selected and dialog closed.
/// Ready to handle user file selection requests.
///
/// # Returns
/// A new `RomLoader` instance ready for file operations
pub fn new() -> Self {
Self {
show_file_dialog: false,
selected_file: None,
}
}
/// Render the ROM loader interface
///
/// Creates the file management interface with load button and
/// file selection dialog integration.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `_state` - GUI state (currently unused but reserved for future integration)
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────┐
/// │ ROM Management [─] [□]│
/// │ [Load ROM File...] │
/// │ Selected: /path/to/program.bin │
/// │ │
/// │ [File Dialog Placeholder] │
/// └─────────────────────────────────────────────────┘
/// ```
pub fn render(&mut self, ui: &mut egui::Ui, _state: &mut GuiState) {
ui.vertical(|ui| {
ui.heading("ROM Management");
// Load button to trigger file selection
if ui.button("Load ROM File...").clicked() {
self.show_file_dialog = true;
}
// Display currently selected file
if let Some(ref file) = self.selected_file.clone() {
ui.label(format!("Selected: {}", file));
}
// File dialog implementation (placeholder)
if self.show_file_dialog {
// Placeholder for file dialog
ui.label("File dialog would open here");
if ui.button("Cancel").clicked() {
self.show_file_dialog = false;
}
}
});
ui.separator();
}
}
/// Status bar component for system status display and health monitoring
///
/// The status bar provides comprehensive system health information,
/// component status indicators, and real-time performance metrics.
/// It serves as the primary location for system state overview.
///
/// ## Features
///
/// - **System Status**: Running/stopped state with visual indicators
/// - **Performance Metrics**: Cycle count and execution speed
/// - **Component Health**: Individual component status monitoring
/// - **Error Display**: Real-time error message display
/// - **System Information**: CPU speed, component count, and configuration
///
/// ## Visual Design
///
/// - **Color Coding**: Green for healthy, red for errors/stopped
/// - **Layout Organization**: Status on left, errors on right
/// - **Real-time Updates**: Live status changes during operation
/// - **Compact Display**: Information-dense but readable layout
///
/// ## Component Status Indicators
///
/// Monitors the health of all major system components:
/// - **CPU**: Intel 4004 execution status
/// - **RAM**: Intel 4002 memory operations
/// - **ROM**: Intel 4001 program storage
/// - **CLK**: System clock generation
pub struct StatusBar;
impl StatusBar {
/// Create a new status bar component
///
/// Initializes a stateless status bar ready to display
/// current system health and performance information.
///
/// # Returns
/// A new `StatusBar` instance
pub fn new() -> Self {
Self
}
/// Render the status bar interface
///
/// Creates a comprehensive status display with system information,
/// component health indicators, and error reporting.
///
/// # Arguments
/// * `ui` - egui UI context for rendering
/// * `state` - Immutable reference to GUI state for status information
///
/// # Layout Structure
/// ```text
/// ┌─────────────────────────────────────────────────────────────┐
/// │ Status: ● Running │ Cycles: 12345 │ CPU: 0.7 MHz │
/// │ Components: 4 │ CPU RAM ROM CLK │ Error: Connection │
/// └─────────────────────────────────────────────────────────────┘
/// ```
pub fn render(&self, ui: &mut egui::Ui, state: &GuiState) {
ui.separator();
ui.horizontal(|ui| {
// Left side: Status information and metrics
ui.with_layout(egui::Layout::left_to_right(egui::Align::Center), |ui| {
// System running status
ui.label("Status:");
if state.system_running {
ui.colored_label(egui::Color32::GREEN, "● Running");
} else {
ui.colored_label(egui::Color32::RED, "● Stopped");
}
ui.separator();
// Performance metrics
ui.label(format!("Cycles: {}", state.cycle_count));
ui.separator();
// System information
if let Some(ref info) = state.system_info {
ui.label(format!("CPU: {:.1} MHz", info.cpu_speed / 1_000_000.0));
ui.label(format!("Components: {}", info.component_count));
}
ui.separator();
// Component status indicators
ui.label("Components:");
ui.colored_label(
if state.component_states.cpu_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"CPU",
);
ui.colored_label(
if state.component_states.ram_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"RAM",
);
ui.colored_label(
if state.component_states.rom_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"ROM",
);
ui.colored_label(
if state.component_states.clock_running {
egui::Color32::GREEN
} else {
egui::Color32::RED
},
"CLK",
);
});
// Right side: Error messages
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if let Some(ref error) = state.last_error {
ui.colored_label(egui::Color32::RED, format!("Error: {}", error));
}
});
});
}
}