diff --git a/game/editor/src/client.rs b/game/editor/src/client.rs index 58d704b..22a05fb 100644 --- a/game/editor/src/client.rs +++ b/game/editor/src/client.rs @@ -1,4 +1,7 @@ -use std::sync::{atomic::AtomicBool, Arc}; +use std::{ + collections::VecDeque, + sync::{atomic::AtomicBool, Arc}, +}; use anyhow::anyhow; use base::system::System; @@ -39,6 +42,8 @@ pub struct EditorClient { pub(crate) clients: Vec, pub(crate) server_id: u64, + pub(crate) msgs: VecDeque<(String, String)>, + mapper_name: String, color: [u8; 3], } @@ -71,6 +76,7 @@ impl EditorClient { clients: Default::default(), server_id: Default::default(), + msgs: Default::default(), mapper_name: mapper_name.unwrap_or_else(|| "mapper".to_string()), color: color.unwrap_or([255, 255, 255]), @@ -155,6 +161,12 @@ impl EditorClient { EditorEventServerToClient::Info { server_id } => { self.server_id = server_id; } + EditorEventServerToClient::Chat { from, msg } => { + self.notifications + .push(EditorNotification::Info(format!("{from}: {msg}"))); + self.msgs.push_back((from, msg)); + self.msgs.truncate(30); + } }, EditorNetEvent::Editor(EditorEvent::Client(_)) => { @@ -210,4 +222,9 @@ impl EditorClient { }, ))); } + + pub fn send_chat(&self, msg: String) { + self.network + .send(EditorEvent::Client(EditorEventClientToServer::Chat { msg })); + } } diff --git a/game/editor/src/editor.rs b/game/editor/src/editor.rs index 2ef0d12..d79523e 100644 --- a/game/editor/src/editor.rs +++ b/game/editor/src/editor.rs @@ -107,7 +107,7 @@ use crate::{ finish_physics_layer_buffer, upload_design_quad_layer_buffer, upload_design_tile_layer_buffer, upload_physics_layer_buffer, }, - notifications::EditorNotifications, + notifications::{EditorNotification, EditorNotifications}, server::EditorServer, tab::EditorTab, tools::{ @@ -2396,6 +2396,11 @@ impl Editor { } } } + EditorUiEvent::Chat { msg } => { + if let Some(tab) = self.tabs.get(&self.active_tab) { + tab.client.send_chat(msg); + } + } } } (unused_rect, input_state, ui_canvas, egui_output) @@ -2508,6 +2513,19 @@ impl EditorInterface for Editor { std::mem::swap(&mut self.save_tasks, &mut unfinished_tasks); // render the overlay for notifications + for ev in self.notifications.take() { + match ev { + EditorNotification::Error(msg) => self + .notifications_overlay + .add_err(msg, Duration::from_secs(10)), + EditorNotification::Warning(msg) => self + .notifications_overlay + .add_warn(msg, Duration::from_secs(10)), + EditorNotification::Info(msg) => self + .notifications_overlay + .add_info(msg, Duration::from_secs(10)), + } + } self.notifications_overlay.render(); if self.is_closed { diff --git a/game/editor/src/event.rs b/game/editor/src/event.rs index 012ba66..5dbc706 100644 --- a/game/editor/src/event.rs +++ b/game/editor/src/event.rs @@ -61,6 +61,9 @@ pub enum EditorEventClientToServer { }, Command(EditorCommand), Info(ClientProps), + Chat { + msg: String, + }, } /// editor events are a collection of either actions or commands @@ -72,6 +75,7 @@ pub enum EditorEventServerToClient { Map(EditorEventOverwriteMap), Infos(Vec), Info { server_id: u64 }, + Chat { from: String, msg: String }, } /// editor events are a collection of either actions or commands diff --git a/game/editor/src/map.rs b/game/editor/src/map.rs index f7d2788..dd0eeb0 100644 --- a/game/editor/src/map.rs +++ b/game/editor/src/map.rs @@ -536,11 +536,17 @@ pub enum EditorGroupPanelTab { Sounds(EditorGroupPanelResources), } +#[derive(Debug, Clone, Default)] +pub struct EditorChatState { + pub msg: String, +} + #[derive(Debug, Clone)] pub struct EditorMapPropsUiValues { pub group_panel_active_tab: EditorGroupPanelTab, pub animations_panel_open: bool, pub server_settings_open: bool, + pub chat_panel_open: Option, pub timeline: Timeline, } @@ -550,6 +556,7 @@ impl Default for EditorMapPropsUiValues { group_panel_active_tab: EditorGroupPanelTab::GroupsAndLayers, animations_panel_open: false, server_settings_open: false, + chat_panel_open: None, timeline: Timeline::new(), } } diff --git a/game/editor/src/server.rs b/game/editor/src/server.rs index 963cb27..4344dd5 100644 --- a/game/editor/src/server.rs +++ b/game/editor/src/server.rs @@ -388,6 +388,14 @@ impl EditorServer { self.broadcast_client_infos(); } + EditorEventClientToServer::Chat { msg } => { + self.network.send(EditorEvent::Server( + EditorEventServerToClient::Chat { + from: client.props.mapper_name.clone(), + msg, + }, + )); + } } } } diff --git a/game/editor/src/ui/chat_panel/mod.rs b/game/editor/src/ui/chat_panel/mod.rs new file mode 100644 index 0000000..7a6b958 --- /dev/null +++ b/game/editor/src/ui/chat_panel/mod.rs @@ -0,0 +1 @@ +pub mod panel; diff --git a/game/editor/src/ui/chat_panel/panel.rs b/game/editor/src/ui/chat_panel/panel.rs new file mode 100644 index 0000000..46418e4 --- /dev/null +++ b/game/editor/src/ui/chat_panel/panel.rs @@ -0,0 +1,56 @@ +use egui::Layout; +use ui_base::types::{UiRenderPipe, UiState}; + +use crate::{ + map::EditorChatState, + ui::user_data::{EditorUiEvent, UserDataWithTab}, +}; + +pub fn render(ui: &mut egui::Ui, pipe: &mut UiRenderPipe, ui_state: &mut UiState) { + let map = &mut pipe.user_data.editor_tab.map; + if ui.input(|i| i.modifiers.shift && i.key_pressed(egui::Key::Enter)) { + map.user.ui_values.chat_panel_open = Some(EditorChatState::default()); + } + + let Some(chat_state) = &mut map.user.ui_values.chat_panel_open else { + return; + }; + + let res = { + let mut panel = egui::SidePanel::right("chat_panel") + .resizable(true) + .width_range(300.0..=600.0); + panel = panel.default_width(500.0); + + let mut send_chat = None; + + let res = panel.show_inside(ui, |ui| { + ui.with_layout(Layout::bottom_up(egui::Align::Min), |ui| { + let inp = ui.text_edit_singleline(&mut chat_state.msg); + if inp.lost_focus() { + send_chat = Some(std::mem::take(&mut chat_state.msg)); + } else { + inp.request_focus(); + } + + for (author, msg) in pipe.user_data.editor_tab.client.msgs.iter().rev() { + ui.label(msg); + ui.label(format!("{author}:")); + ui.add_space(10.0); + } + }) + }); + + if let Some(msg) = send_chat { + pipe.user_data.ui_events.push(EditorUiEvent::Chat { msg }); + + map.user.ui_values.chat_panel_open = None; + } + + Some(res) + }; + + if let Some(res) = res { + ui_state.add_blur_rect(res.response.rect, 0.0); + } +} diff --git a/game/editor/src/ui/main_frame.rs b/game/editor/src/ui/main_frame.rs index cb75505..c42a4a2 100644 --- a/game/editor/src/ui/main_frame.rs +++ b/game/editor/src/ui/main_frame.rs @@ -40,6 +40,8 @@ pub fn render(ui: &mut egui::Ui, pipe: &mut UiRenderPipe, ui_state: &m super::group_and_layer::layer_props::render(ui, &mut pipe, ui_state); super::group_and_layer::quad_props::render(ui, &mut pipe, ui_state); super::group_and_layer::sound_props::render(ui, &mut pipe, ui_state); + + super::chat_panel::panel::render(ui, &mut pipe, ui_state); } *pipe.user_data.pointer_is_used |= ui.memory(|i| i.any_popup_open()); diff --git a/game/editor/src/ui/mod.rs b/game/editor/src/ui/mod.rs index 85f438c..08dfb15 100644 --- a/game/editor/src/ui/mod.rs +++ b/game/editor/src/ui/mod.rs @@ -2,6 +2,7 @@ pub mod animation_panel; pub mod auto_mapper; pub mod auto_saver; pub mod bottom_panel; +pub mod chat_panel; pub mod group_and_layer; pub mod left_panel; pub mod main_frame; diff --git a/game/editor/src/ui/user_data.rs b/game/editor/src/ui/user_data.rs index b0421f2..8e45604 100644 --- a/game/editor/src/ui/user_data.rs +++ b/game/editor/src/ui/user_data.rs @@ -58,6 +58,9 @@ pub enum EditorUiEvent { CursorWorldPos { pos: vec2, }, + Chat { + msg: String, + }, } pub struct EditorMenuHostNetworkOptions {