diff --git a/ui/src/main.rs b/ui/src/main.rs index b7948bc..eadfa5f 100644 --- a/ui/src/main.rs +++ b/ui/src/main.rs @@ -126,10 +126,6 @@ enum AppState { } struct ChessApp { - fullscreen: bool, - resolutions: Vec<(u32, u32)>, - pending_settings: PendingSettings, - selected_resolution: usize, state: AppState, game_state: Arc>, server_port: String, @@ -141,13 +137,18 @@ struct ChessApp { rx_from_network: Option>, // UI state selected_square: Option<(usize, usize)>, + //Settings + fullscreen: bool, + resolutions: Vec<(u32, u32)>, + pending_settings: PendingSettings, + selected_resolution: usize, + dark_mode: bool, } #[derive(Default)] struct PendingSettings { fullscreen: bool, selected_resolution: usize, - server_port: String, } impl Default for ChessApp { @@ -163,6 +164,7 @@ impl Default for ChessApp { ], pending_settings: PendingSettings::default(), selected_resolution: 2, + dark_mode: false, state: AppState::MainMenu, game_state: Arc::new(Mutex::new(GameState::default())), server_port: "9001".to_string(), @@ -213,7 +215,6 @@ impl ChessApp { fn apply_settings(&mut self, ctx: &egui::Context) { self.fullscreen = self.pending_settings.fullscreen; self.selected_resolution = self.pending_settings.selected_resolution; - self.server_port = self.pending_settings.server_port.clone(); if let Some(resolution) = self.resolutions.get(self.selected_resolution) { ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize( @@ -439,70 +440,151 @@ impl eframe::App for ChessApp { // Get current game state let game_state = self.game_state.lock().unwrap().clone(); - let screen_size = ctx.screen_rect().size(); let base_size = screen_size.x.min(screen_size.y); + + + // Determine background color based on dark mode setting + let background_color = if self.dark_mode { + egui::Color32::from_rgb(27, 27, 27) // Dark mode + } else { + egui::Color32::from_rgb(235, 235, 235) // Light mode + }; + // Also adjust text colors if needed + let text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + + // Update the visual style based on dark mode + let mut visuals = ctx.style().visuals.clone(); + + if self.dark_mode { + // Dark mode visuals + visuals = egui::Visuals::dark(); + // Adjust specific colors if needed + visuals.widgets.noninteractive.bg_fill = egui::Color32::from_rgb(40, 40, 40); + visuals.widgets.inactive.bg_fill = egui::Color32::from_rgb(60, 60, 60); + visuals.widgets.hovered.bg_fill = egui::Color32::from_rgb(70, 70, 70); + visuals.widgets.active.bg_fill = egui::Color32::from_rgb(80, 80, 80); + visuals.faint_bg_color = egui::Color32::from_rgb(50, 50, 50); + visuals.extreme_bg_color = egui::Color32::from_rgb(20, 20, 20); + visuals.code_bg_color = egui::Color32::from_rgb(40, 40, 40); + visuals.panel_fill = background_color; + } else { + // Light mode visuals + visuals = egui::Visuals::light(); + visuals.widgets.noninteractive.bg_fill = egui::Color32::from_rgb(210, 210, 210); + visuals.widgets.inactive.bg_fill = egui::Color32::from_rgb(190,190,190); + visuals.widgets.hovered.bg_fill = egui::Color32::from_rgb(180,180,180); + visuals.widgets.active.bg_fill = egui::Color32::from_rgb(170,170,170); + visuals.faint_bg_color = egui::Color32::from_rgb(200,200,200); + visuals.extreme_bg_color = egui::Color32::from_rgb(230,230,230); + visuals.code_bg_color = egui::Color32::from_rgb(210,210,210); + visuals.panel_fill = background_color; + } + + // Apply the updated visuals + ctx.set_visuals(visuals); + + match self.state { AppState::MainMenu => { - - - - - //proportional sizing + // Proportional sizing let button_width = base_size*0.4; let button_height = base_size*0.1; let font_size = base_size*0.025; let heading_size=base_size*0.1; let spacing_size = base_size*0.07; - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading("♞ Knightly ♞"); - ui.add_space(30.0); - - ui.horizontal(|ui| { - ui.label("Username:"); - ui.text_edit_singleline(&mut self.username); - }); - - ui.add_space(20.0); - if ui.add_sized( - egui::Vec2::new(button_width, button_height), - egui::Button::new(egui::RichText::new("Online Play").size(font_size)) - ).clicked() { - self.server_ip = "127.0.0.1".to_string(); - self.connect_to_server(); - } - ui.add_space(20.0); - if ui.add_sized( - egui::Vec2::new(button_width, button_height), - egui::Button::new(egui::RichText::new("Private Play").size(font_size)) - ).clicked() { - self.state = AppState::PrivatePlayConnect; - } - ui.add_space(20.0); - if ui.add_sized( - egui::Vec2::new(button_width,button_height), - egui::Button::new(egui::RichText::new("Settings").size(font_size)) - ).clicked(){ - self.state = AppState::Settings; - } + // Set background color for the entire panel + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + // Style the heading based on dark mode + let heading_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; - + ui.heading(egui::RichText::new("♞ Knightly ♞").color(heading_color)); + ui.add_space(30.0); - ui.add_space(20.0); - if ui.add_sized( - egui::Vec2::new(button_width, button_height), - egui::Button::new(egui::RichText::new("Quit").size(font_size)) - ).clicked() { - std::process::exit(0); - } + ui.horizontal(|ui| { + ui.label(egui::RichText::new("Username:").color(heading_color)); + ui.text_edit_singleline(&mut self.username); + }); + + ui.add_space(20.0); + + // Create styled button + let button_text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + + if ui.add_sized( + egui::Vec2::new(button_width, button_height), + egui::Button::new( + egui::RichText::new("Online Play") + .size(font_size) + .color(button_text_color) + ) + ).clicked() { + self.server_ip = "127.0.0.1".to_string(); + self.connect_to_server(); + } + + ui.add_space(20.0); + + if ui.add_sized( + egui::Vec2::new(button_width, button_height), + egui::Button::new( + egui::RichText::new("Private Play") + .size(font_size) + .color(button_text_color) + ) + ).clicked() { + self.state = AppState::PrivatePlayConnect; + } + + ui.add_space(20.0); + + if ui.add_sized( + egui::Vec2::new(button_width, button_height), + egui::Button::new( + egui::RichText::new("Settings") + .size(font_size) + .color(button_text_color) + ) + ).clicked() { + self.state = AppState::Settings; + } + + ui.add_space(20.0); + + if ui.add_sized( + egui::Vec2::new(button_width, button_height), + egui::Button::new( + egui::RichText::new("Quit") + .size(font_size) + .color(button_text_color) + ) + ).clicked() { + std::process::exit(0); + } + }); }); - }); } AppState::Settings => { - egui::CentralPanel::default().show(ctx, |ui| { + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { ui.vertical_centered(|ui| { ui.heading("Settings"); ui.add_space(30.0); @@ -535,8 +617,14 @@ impl eframe::App for ChessApp { } }); }); - ui.add_space(30.0); - + ui.add_space(10.0); + //dark mode toggle + ui.horizontal(|ui| { + ui.label("Dark mode"); + if ui.checkbox(&mut self.dark_mode, "").changed() { + info!("Dark mode changed to: {}", self.dark_mode); + } + }); // Apply and Cancel buttons ui.horizontal(|ui| { if ui.add_sized([140.0, 40.0], egui::Button::new("Apply")).clicked() { @@ -552,122 +640,174 @@ impl eframe::App for ChessApp { }); } AppState::PrivatePlayConnect => { - let button_width = base_size*0.4; - let button_height = base_size*0.1; - let font_size = base_size*0.025; - let heading_size=base_size*0.1; - let spacing_size = base_size*0.07; - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.horizontal(|ui| { - ui.label("Server ip address"); - ui.text_edit_singleline(&mut self.server_ip); - }); - - ui.horizontal(|ui| { - ui.label("Server Port:"); - ui.text_edit_singleline(&mut self.server_port); - }); - - ui.horizontal(|ui| { - ui.checkbox(&mut self.start_local_server_instance, "Host Server"); - }); - - ui.add_space(20.0); - if ui.add_sized( - egui::Vec2::new(button_width, button_height), - egui::Button::new(egui::RichText::new("Play").size(font_size)) - ).clicked() { - if self.start_local_server_instance == true { - let path = if cfg!(windows) { - "./server.exe" - } else { - "./server" - }; - - if !Path::new(path).exists() { - error!("Server binary does not exist, cfg: {}", path); - } else { - let _ = Command::new(path).spawn(); - std::thread::sleep(std::time::Duration::from_secs(1)); - } - } - self.connect_to_server(); - } - ui.add_space(20.0); - if ui.add_sized( - egui::Vec2::new(button_width, button_height), - egui::Button::new(egui::RichText::new("Return to main menu").size(font_size)) - ).clicked(){ - self.state=AppState::MainMenu; - } - }) + let button_width = base_size*0.4; + let button_height = base_size*0.1; + let font_size = base_size*0.025; + + let text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + ui.horizontal(|ui| { + ui.label(egui::RichText::new("Server ip address").color(text_color)); + ui.text_edit_singleline(&mut self.server_ip); }); - } + + ui.horizontal(|ui| { + ui.label(egui::RichText::new("Server Port:").color(text_color)); + ui.text_edit_singleline(&mut self.server_port); + }); + + ui.horizontal(|ui| { + ui.label(egui::RichText::new("Host Server").color(text_color)); + ui.checkbox(&mut self.start_local_server_instance, ""); + }); + + ui.add_space(20.0); + if ui.add_sized( + egui::Vec2::new(button_width, button_height), + egui::Button::new( + egui::RichText::new("Play") + .size(font_size) + .color(text_color) + ) + ).clicked() { + if self.start_local_server_instance == true { + let path = if cfg!(windows) { + "./server.exe" + } else { + "./server" + }; + + if !Path::new(path).exists() { + error!("Server binary does not exist, cfg: {}", path); + } else { + let _ = Command::new(path).spawn(); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + } + self.connect_to_server(); + } + ui.add_space(20.0); + if ui.add_sized( + egui::Vec2::new(button_width, button_height), + egui::Button::new( + egui::RichText::new("Return to main menu") + .size(font_size) + .color(text_color) + ) + ).clicked(){ + self.state=AppState::MainMenu; + } + }) + }); +} AppState::Connecting => { - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading("Connecting to Server..."); - ui.add_space(20.0); - ui.spinner(); - }); + let text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + ui.heading(egui::RichText::new("Connecting to Server...").color(text_color)); + ui.add_space(20.0); + ui.spinner(); - if ui.button("Cancel").clicked() { - info!("Returning to menu from before connecting to the server"); - self.state = AppState::MainMenu; - } - }); - } + ui.add_space(20.0); + if ui.button( + egui::RichText::new("Cancel").color(text_color) + ).clicked() { + info!("Returning to menu from before connecting to the server"); + self.state = AppState::MainMenu; + } + }); + }); +} AppState::FindingMatch => { - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading("Finding Match..."); - ui.add_space(20.0); - ui.label("Waiting for an opponent..."); - ui.spinner(); + let text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + ui.heading(egui::RichText::new("Finding Match...").color(text_color)); + ui.add_space(20.0); + ui.label(egui::RichText::new("Waiting for an opponent...").color(text_color)); + ui.spinner(); - ui.add_space(20.0); - if ui.button("cancel").clicked() { - if let Some(tx) = &self.tx_to_network { - warn!("Closing connection to server, cancelled match findig!"); - tx.send(ClientEvent::CloseConnection); - self.state = AppState::MainMenu; - } - } - }); - }); - } + ui.add_space(20.0); + if ui.button( + egui::RichText::new("cancel").color(text_color) + ).clicked() { + if let Some(tx) = &self.tx_to_network { + warn!("Closing connection to server, cancelled match finding!"); + let _ = tx.send(ClientEvent::CloseConnection); + self.state = AppState::MainMenu; + } + } + }); + }); +} AppState::InGame => { + let text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + // Draw menu bar - egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { - ui.horizontal(|ui| { - if ui.button("Main Menu").clicked() { - *self = ChessApp::default(); - } - - if ui.button("Resign").clicked() { - if let Some(tx) = &self.tx_to_network { - let _ = tx.send(ClientEvent::Resign); + egui::TopBottomPanel::top("menu_bar") + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { + ui.horizontal(|ui| { + if ui.button( + egui::RichText::new("Main Menu").color(text_color) + ).clicked() { + *self = ChessApp::default(); } - } - ui.separator(); + if ui.button( + egui::RichText::new("Resign").color(text_color) + ).clicked() { + if let Some(tx) = &self.tx_to_network { + let _ = tx.send(ClientEvent::Resign); + } + } - if let Some(color) = &game_state.player_color { - ui.label(format!("You are: {}", color)); - } + ui.separator(); - if let Some(opponent) = &game_state.opponent_name { - ui.label(format!("vs: {}", opponent)); - } + if let Some(color) = &game_state.player_color { + ui.label(egui::RichText::new(format!("You are: {}", color)).color(text_color)); + } + + if let Some(opponent) = &game_state.opponent_name { + ui.label(egui::RichText::new(format!("vs: {}", opponent)).color(text_color)); + } + }); }); - }); + // Main content area with chess board and move history - egui::CentralPanel::default().show(ctx, |ui| { + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { let total_width = ui.available_width(); let total_height = ui.available_height(); @@ -773,7 +913,11 @@ impl eframe::App for ChessApp { // Move History (right side) - match the board height ui.vertical(|ui| { egui::Frame::default() - .fill(egui::Color32::from_rgb(240, 240, 240)) + .fill(if self.dark_mode { + egui::Color32::from_rgb(60, 60, 60) + } else { + egui::Color32::from_rgb(240, 240, 240) + }) .stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(200, 200, 200))) .inner_margin(egui::Margin::same(8)) .show(ui, |ui| { @@ -786,27 +930,37 @@ impl eframe::App for ChessApp { // Scroll area for move history egui::ScrollArea::vertical() - .max_height(board_size - 50.0) // Based on board height - .show(ui, |ui| { - // Use actual move history from game_state - if let Ok(game_state) = self.game_state.lock() { - for (i, move_text) in game_state.move_history.iter().enumerate() { - ui.horizontal(|ui| { - // Alternate background - if i % 2 == 0 { - ui.visuals_mut().widgets.noninteractive.bg_fill = - egui::Color32::from_rgb(250, 250, 250); - } else { - ui.visuals_mut().widgets.noninteractive.bg_fill = - egui::Color32::from_rgb(230, 230, 230); - } - - ui.label(egui::RichText::new(move_text.to_string()).size(16.0)); - - if ui.small_button("📋").clicked() { - info!("Copy move: {}", move_text); - } - }); + .max_height(board_size - 50.0) + .show(ui, |ui| { + if let Ok(game_state) = self.game_state.lock() { + for (i, move_text) in game_state.move_history.iter().enumerate() { + ui.horizontal(|ui| { + // Alternate background based on dark mode + if i % 2 == 0 { + ui.visuals_mut().widgets.noninteractive.bg_fill = + if self.dark_mode { + egui::Color32::from_rgb(70, 70, 70) + } else { + egui::Color32::from_rgb(250, 250, 250) + }; + } else { + ui.visuals_mut().widgets.noninteractive.bg_fill = + if self.dark_mode { + egui::Color32::from_rgb(50, 50, 50) + } else { + egui::Color32::from_rgb(230, 230, 230) + }; + } + + // Move text color + ui.label(egui::RichText::new(move_text.to_string()) + .size(16.0) + .color(text_color)); + + if ui.small_button("📋").clicked() { + info!("Copy move: {}", move_text); + } + }); if i < game_state.move_history.len() - 1 { ui.add_space(2.0); @@ -814,11 +968,15 @@ impl eframe::App for ChessApp { } if game_state.move_history.is_empty() { - ui.vertical_centered(|ui| { - ui.add_space(20.0); - ui.label(egui::RichText::new("No moves yet").size(16.0)); - ui.label(egui::RichText::new("Game will start soon...").size(14.0)); - }); + ui.vertical_centered(|ui| { + ui.add_space(20.0); + ui.label(egui::RichText::new("No moves yet") + .size(16.0) + .color(text_color)); + ui.label(egui::RichText::new("Game will start soon...") + .size(14.0) + .color(text_color)); + }); } } }); @@ -831,23 +989,33 @@ impl eframe::App for ChessApp { } AppState::GameOver => { - egui::CentralPanel::default().show(ctx, |ui| { - ui.vertical_centered(|ui| { - ui.heading("Game Over"); - ui.add_space(20.0); + let text_color = if self.dark_mode { + egui::Color32::WHITE + } else { + egui::Color32::BLACK + }; + + egui::CentralPanel::default() + .frame(egui::Frame::default().fill(background_color)) + .show(ctx, |ui| { + ui.vertical_centered(|ui| { + ui.heading(egui::RichText::new("Game Over").color(text_color)); + ui.add_space(20.0); - if let Some(reason) = &game_state.game_over { - ui.label(format!("Result: {}", reason)); - } + if let Some(reason) = &game_state.game_over { + ui.label(egui::RichText::new(format!("Result: {}", reason)).color(text_color)); + } - ui.add_space(20.0); + ui.add_space(20.0); - if ui.button("Back to Main Menu").clicked() { - *self = ChessApp::default(); - } - }); - }); - } + if ui.button( + egui::RichText::new("Back to Main Menu").color(text_color) + ).clicked() { + *self = ChessApp::default(); + } + }); + }); +} } // Request repaint to keep UI responsive