Added dark and light modes

This commit is contained in:
Bence
2025-12-02 13:08:34 +01:00
parent 9444441c07
commit ac2fe4418c

View File

@@ -126,10 +126,6 @@ enum AppState {
} }
struct ChessApp { struct ChessApp {
fullscreen: bool,
resolutions: Vec<(u32, u32)>,
pending_settings: PendingSettings,
selected_resolution: usize,
state: AppState, state: AppState,
game_state: Arc<Mutex<GameState>>, game_state: Arc<Mutex<GameState>>,
server_port: String, server_port: String,
@@ -141,13 +137,18 @@ struct ChessApp {
rx_from_network: Option<mpsc::UnboundedReceiver<ServerMessage2>>, rx_from_network: Option<mpsc::UnboundedReceiver<ServerMessage2>>,
// UI state // UI state
selected_square: Option<(usize, usize)>, selected_square: Option<(usize, usize)>,
//Settings
fullscreen: bool,
resolutions: Vec<(u32, u32)>,
pending_settings: PendingSettings,
selected_resolution: usize,
dark_mode: bool,
} }
#[derive(Default)] #[derive(Default)]
struct PendingSettings { struct PendingSettings {
fullscreen: bool, fullscreen: bool,
selected_resolution: usize, selected_resolution: usize,
server_port: String,
} }
impl Default for ChessApp { impl Default for ChessApp {
@@ -163,6 +164,7 @@ impl Default for ChessApp {
], ],
pending_settings: PendingSettings::default(), pending_settings: PendingSettings::default(),
selected_resolution: 2, selected_resolution: 2,
dark_mode: false,
state: AppState::MainMenu, state: AppState::MainMenu,
game_state: Arc::new(Mutex::new(GameState::default())), game_state: Arc::new(Mutex::new(GameState::default())),
server_port: "9001".to_string(), server_port: "9001".to_string(),
@@ -213,7 +215,6 @@ impl ChessApp {
fn apply_settings(&mut self, ctx: &egui::Context) { fn apply_settings(&mut self, ctx: &egui::Context) {
self.fullscreen = self.pending_settings.fullscreen; self.fullscreen = self.pending_settings.fullscreen;
self.selected_resolution = self.pending_settings.selected_resolution; 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) { if let Some(resolution) = self.resolutions.get(self.selected_resolution) {
ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize( ctx.send_viewport_cmd(egui::ViewportCommand::InnerSize(
@@ -439,70 +440,151 @@ impl eframe::App for ChessApp {
// Get current game state // Get current game state
let game_state = self.game_state.lock().unwrap().clone(); let game_state = self.game_state.lock().unwrap().clone();
let screen_size = ctx.screen_rect().size(); let screen_size = ctx.screen_rect().size();
let base_size = screen_size.x.min(screen_size.y); 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 { match self.state {
AppState::MainMenu => { AppState::MainMenu => {
// Proportional sizing
//proportional sizing
let button_width = base_size*0.4; let button_width = base_size*0.4;
let button_height = base_size*0.1; let button_height = base_size*0.1;
let font_size = base_size*0.025; let font_size = base_size*0.025;
let heading_size=base_size*0.1; let heading_size=base_size*0.1;
let spacing_size = base_size*0.07; let spacing_size = base_size*0.07;
egui::CentralPanel::default().show(ctx, |ui| { // Set background color for the entire panel
ui.vertical_centered(|ui| { egui::CentralPanel::default()
ui.heading("♞ Knightly ♞"); .frame(egui::Frame::default().fill(background_color))
ui.add_space(30.0); .show(ctx, |ui| {
ui.vertical_centered(|ui| {
ui.horizontal(|ui| { // Style the heading based on dark mode
ui.label("Username:"); let heading_color = if self.dark_mode {
ui.text_edit_singleline(&mut self.username); egui::Color32::WHITE
}); } else {
egui::Color32::BLACK
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;
}
ui.heading(egui::RichText::new("♞ Knightly ♞").color(heading_color));
ui.add_space(30.0);
ui.add_space(20.0); ui.horizontal(|ui| {
if ui.add_sized( ui.label(egui::RichText::new("Username:").color(heading_color));
egui::Vec2::new(button_width, button_height), ui.text_edit_singleline(&mut self.username);
egui::Button::new(egui::RichText::new("Quit").size(font_size)) });
).clicked() {
std::process::exit(0); 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 => { 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.vertical_centered(|ui| {
ui.heading("Settings"); ui.heading("Settings");
ui.add_space(30.0); 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 // Apply and Cancel buttons
ui.horizontal(|ui| { ui.horizontal(|ui| {
if ui.add_sized([140.0, 40.0], egui::Button::new("Apply")).clicked() { if ui.add_sized([140.0, 40.0], egui::Button::new("Apply")).clicked() {
@@ -552,122 +640,174 @@ impl eframe::App for ChessApp {
}); });
} }
AppState::PrivatePlayConnect => { AppState::PrivatePlayConnect => {
let button_width = base_size*0.4; let button_width = base_size*0.4;
let button_height = base_size*0.1; let button_height = base_size*0.1;
let font_size = base_size*0.025; let font_size = base_size*0.025;
let heading_size=base_size*0.1;
let spacing_size = base_size*0.07; let text_color = if self.dark_mode {
egui::CentralPanel::default().show(ctx, |ui| { egui::Color32::WHITE
ui.vertical_centered(|ui| { } else {
ui.horizontal(|ui| { egui::Color32::BLACK
ui.label("Server ip address"); };
ui.text_edit_singleline(&mut self.server_ip);
}); egui::CentralPanel::default()
.frame(egui::Frame::default().fill(background_color))
ui.horizontal(|ui| { .show(ctx, |ui| {
ui.label("Server Port:"); ui.vertical_centered(|ui| {
ui.text_edit_singleline(&mut self.server_port); 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.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;
}
})
}); });
}
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 => { AppState::Connecting => {
egui::CentralPanel::default().show(ctx, |ui| { let text_color = if self.dark_mode {
ui.vertical_centered(|ui| { egui::Color32::WHITE
ui.heading("Connecting to Server..."); } else {
ui.add_space(20.0); egui::Color32::BLACK
ui.spinner(); };
});
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() { ui.add_space(20.0);
info!("Returning to menu from before connecting to the server"); if ui.button(
self.state = AppState::MainMenu; egui::RichText::new("Cancel").color(text_color)
} ).clicked() {
}); info!("Returning to menu from before connecting to the server");
} self.state = AppState::MainMenu;
}
});
});
}
AppState::FindingMatch => { AppState::FindingMatch => {
egui::CentralPanel::default().show(ctx, |ui| { let text_color = if self.dark_mode {
ui.vertical_centered(|ui| { egui::Color32::WHITE
ui.heading("Finding Match..."); } else {
ui.add_space(20.0); egui::Color32::BLACK
ui.label("Waiting for an opponent..."); };
ui.spinner();
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); ui.add_space(20.0);
if ui.button("cancel").clicked() { if ui.button(
if let Some(tx) = &self.tx_to_network { egui::RichText::new("cancel").color(text_color)
warn!("Closing connection to server, cancelled match findig!"); ).clicked() {
tx.send(ClientEvent::CloseConnection); if let Some(tx) = &self.tx_to_network {
self.state = AppState::MainMenu; warn!("Closing connection to server, cancelled match finding!");
} let _ = tx.send(ClientEvent::CloseConnection);
} self.state = AppState::MainMenu;
}); }
}); }
} });
});
}
AppState::InGame => { AppState::InGame => {
let text_color = if self.dark_mode {
egui::Color32::WHITE
} else {
egui::Color32::BLACK
};
// Draw menu bar // Draw menu bar
egui::TopBottomPanel::top("menu_bar").show(ctx, |ui| { egui::TopBottomPanel::top("menu_bar")
ui.horizontal(|ui| { .frame(egui::Frame::default().fill(background_color))
if ui.button("Main Menu").clicked() { .show(ctx, |ui| {
*self = ChessApp::default(); ui.horizontal(|ui| {
} if ui.button(
egui::RichText::new("Main Menu").color(text_color)
if ui.button("Resign").clicked() { ).clicked() {
if let Some(tx) = &self.tx_to_network { *self = ChessApp::default();
let _ = tx.send(ClientEvent::Resign);
} }
}
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.separator();
ui.label(format!("You are: {}", color));
}
if let Some(opponent) = &game_state.opponent_name { if let Some(color) = &game_state.player_color {
ui.label(format!("vs: {}", opponent)); 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 // 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_width = ui.available_width();
let total_height = ui.available_height(); let total_height = ui.available_height();
@@ -773,7 +913,11 @@ impl eframe::App for ChessApp {
// Move History (right side) - match the board height // Move History (right side) - match the board height
ui.vertical(|ui| { ui.vertical(|ui| {
egui::Frame::default() 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))) .stroke(egui::Stroke::new(1.0, egui::Color32::from_rgb(200, 200, 200)))
.inner_margin(egui::Margin::same(8)) .inner_margin(egui::Margin::same(8))
.show(ui, |ui| { .show(ui, |ui| {
@@ -786,27 +930,37 @@ impl eframe::App for ChessApp {
// Scroll area for move history // Scroll area for move history
egui::ScrollArea::vertical() egui::ScrollArea::vertical()
.max_height(board_size - 50.0) // Based on board height .max_height(board_size - 50.0)
.show(ui, |ui| { .show(ui, |ui| {
// Use actual move history from game_state if let Ok(game_state) = self.game_state.lock() {
if let Ok(game_state) = self.game_state.lock() { for (i, move_text) in game_state.move_history.iter().enumerate() {
for (i, move_text) in game_state.move_history.iter().enumerate() { ui.horizontal(|ui| {
ui.horizontal(|ui| { // Alternate background based on dark mode
// Alternate background if i % 2 == 0 {
if i % 2 == 0 { ui.visuals_mut().widgets.noninteractive.bg_fill =
ui.visuals_mut().widgets.noninteractive.bg_fill = if self.dark_mode {
egui::Color32::from_rgb(250, 250, 250); egui::Color32::from_rgb(70, 70, 70)
} else { } else {
ui.visuals_mut().widgets.noninteractive.bg_fill = egui::Color32::from_rgb(250, 250, 250)
egui::Color32::from_rgb(230, 230, 230); };
} } else {
ui.visuals_mut().widgets.noninteractive.bg_fill =
ui.label(egui::RichText::new(move_text.to_string()).size(16.0)); if self.dark_mode {
egui::Color32::from_rgb(50, 50, 50)
if ui.small_button("📋").clicked() { } else {
info!("Copy move: {}", move_text); 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 { if i < game_state.move_history.len() - 1 {
ui.add_space(2.0); ui.add_space(2.0);
@@ -814,11 +968,15 @@ impl eframe::App for ChessApp {
} }
if game_state.move_history.is_empty() { if game_state.move_history.is_empty() {
ui.vertical_centered(|ui| { ui.vertical_centered(|ui| {
ui.add_space(20.0); ui.add_space(20.0);
ui.label(egui::RichText::new("No moves yet").size(16.0)); ui.label(egui::RichText::new("No moves yet")
ui.label(egui::RichText::new("Game will start soon...").size(14.0)); .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 => { AppState::GameOver => {
egui::CentralPanel::default().show(ctx, |ui| { let text_color = if self.dark_mode {
ui.vertical_centered(|ui| { egui::Color32::WHITE
ui.heading("Game Over"); } else {
ui.add_space(20.0); 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 { if let Some(reason) = &game_state.game_over {
ui.label(format!("Result: {}", reason)); 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() { if ui.button(
*self = ChessApp::default(); egui::RichText::new("Back to Main Menu").color(text_color)
} ).clicked() {
}); *self = ChessApp::default();
}); }
} });
});
}
} }
// Request repaint to keep UI responsive // Request repaint to keep UI responsive