use crate::mosaic::behavior::{TileBehavior, TileBehaviorWithWorld};
use crate::mosaic::modal::{ModalData, ModalKind, ModalResult, Modals};
use crate::mosaic::widget::{Pane, Widget, WidgetContext};
use crate::prelude::*;
use super::Mosaic;
impl<W: Widget<C, M>, C, M> Mosaic<W, C, M> {
pub fn show(&mut self, world: &mut World, ui_context: &egui::Context, app: &mut C) {
self.prepare(world);
let mut tree = self.tile_tree.take();
egui::CentralPanel::default()
.frame(egui::Frame::NONE)
.show(ui_context, |ui| {
Self::render_tree(
&mut tree,
&mut self.behavior,
world,
app,
&mut self.modals,
(&mut self.pending_messages, &mut self.incoming_messages),
ui,
);
});
self.render_modals(ui_context);
self.process_pending_removals(world, app);
self.finish(tree);
}
pub fn show_inside(&mut self, world: &mut World, ui: &mut egui::Ui, app: &mut C) {
self.prepare(world);
let ui_context = ui.ctx().clone();
let mut tree = self.tile_tree.take();
Self::render_tree(
&mut tree,
&mut self.behavior,
world,
app,
&mut self.modals,
(&mut self.pending_messages, &mut self.incoming_messages),
ui,
);
self.render_modals(&ui_context);
self.process_pending_removals(world, app);
self.finish(tree);
}
fn render_tree(
tree: &mut Option<egui_tiles::Tree<Pane<W>>>,
behavior: &mut TileBehavior<W, C, M>,
world: &mut World,
app: &mut C,
modals: &mut Modals,
messages: (
&mut Vec<M>,
&mut std::collections::HashMap<egui_tiles::TileId, Vec<M>>,
),
ui: &mut egui::Ui,
) {
let (pending, incoming) = messages;
if let Some(tree) = tree {
{
let mut behavior_with_world = TileBehaviorWithWorld {
behavior: &mut *behavior,
world: &mut *world,
app: &mut *app,
modals: &mut *modals,
messages: &mut *pending,
incoming_messages: &mut *incoming,
};
tree.ui(&mut behavior_with_world, ui);
}
Self::process_pending_add(behavior, tree, modals, app, world, pending, incoming);
}
}
pub(super) fn prepare(&mut self, world: &mut World) {
self.behavior.viewport_rects.clear();
self.behavior.viewport_textures.clear();
let source_textures = self
.viewport_texture_overrides
.as_ref()
.unwrap_or(&world.resources.user_interface.viewport_textures);
self.behavior
.viewport_textures
.extend_from_slice(source_textures);
self.behavior.config = self.config.clone();
self.behavior.window_index = self.window_index;
self.behavior.is_active_window = self.is_active_window;
self.behavior.cached_cameras.clear();
self.behavior
.cached_cameras
.extend(world.query_entities(CAMERA));
self.behavior.cached_cameras.sort_by_key(|entity| entity.id);
if let Some(tree) = &self.tile_tree {
for (_tile_id, tile) in tree.tiles.iter() {
if let egui_tiles::Tile::Pane(pane) = tile
&& let Some(camera_entity) =
pane.widget.required_camera(&self.behavior.cached_cameras)
&& !world
.resources
.user_interface
.required_cameras
.contains(&camera_entity)
{
world
.resources
.user_interface
.required_cameras
.push(camera_entity);
world
.resources
.user_interface
.required_camera_sizes
.push((0, 0));
}
}
}
}
pub(super) fn finish(&mut self, tree: Option<egui_tiles::Tree<Pane<W>>>) {
self.tile_tree = tree;
self.incoming_messages.clear();
if self.behavior.layout_modified {
self.layout_modified = true;
self.behavior.layout_modified = false;
}
}
fn process_pending_add(
behavior: &mut TileBehavior<W, C, M>,
tree: &mut egui_tiles::Tree<Pane<W>>,
modals: &mut Modals,
app: &mut C,
world: &mut World,
messages: &mut Vec<M>,
incoming_messages: &mut std::collections::HashMap<egui_tiles::TileId, Vec<M>>,
) {
if let Some((tile_id, catalog_index)) = behavior.pending_add_widget.take() {
let catalog = W::catalog();
if let Some(entry) = catalog.get(catalog_index) {
let mut new_widget = (entry.create)();
let new_pane = Pane::new(new_widget.clone());
let new_tile_id = tree.tiles.insert_pane(new_pane);
Self::add_to_container(&mut tree.tiles, tile_id, new_tile_id);
let mut context = WidgetContext {
world,
selected_viewport_tile: &mut behavior.selected_viewport_tile,
viewport_textures: &behavior.viewport_textures,
current_tile_id: new_tile_id,
modals,
app,
window_index: behavior.window_index,
is_active_window: behavior.is_active_window,
cached_cameras: &behavior.cached_cameras,
messages,
incoming_messages,
};
new_widget.on_add(&mut context);
if let Some(egui_tiles::Tile::Pane(pane)) = tree.tiles.get_mut(new_tile_id) {
pane.widget = new_widget;
}
}
}
}
fn process_pending_removals(&mut self, world: &mut World, app: &mut C) {
let removals = std::mem::take(&mut self.behavior.pending_removals);
for (tile_id, mut widget) in removals {
let mut context = WidgetContext {
world: &mut *world,
selected_viewport_tile: &mut self.behavior.selected_viewport_tile,
viewport_textures: &self.behavior.viewport_textures,
current_tile_id: tile_id,
modals: &mut self.modals,
app: &mut *app,
window_index: self.behavior.window_index,
is_active_window: self.behavior.is_active_window,
cached_cameras: &self.behavior.cached_cameras,
messages: &mut self.pending_messages,
incoming_messages: &mut self.incoming_messages,
};
widget.on_remove(&mut context);
}
}
pub(super) fn render_modals(&mut self, ui_context: &egui::Context) {
self.modals.activate_next_pending();
if let Some(mut modal_data) = self.modals.active.take() {
let mut should_close = false;
let mut confirmed = false;
egui::Area::new(egui::Id::new("mosaic_modal_backdrop"))
.fixed_pos(egui::pos2(0.0, 0.0))
.order(egui::Order::Middle)
.show(ui_context, |ui| {
let screen = ui.ctx().content_rect();
ui.painter()
.rect_filled(screen, 0.0, egui::Color32::from_black_alpha(128));
ui.allocate_rect(screen, egui::Sense::click());
});
egui::Window::new(&modal_data.title)
.collapsible(false)
.resizable(false)
.order(egui::Order::Foreground)
.anchor(egui::Align2::CENTER_CENTER, egui::vec2(0.0, 0.0))
.show(ui_context, |ui| {
ui.add_space(8.0);
ui.label(&modal_data.body);
ui.add_space(8.0);
match &mut modal_data.kind {
ModalKind::Confirm => {}
ModalKind::TextInput { current_text } => {
let response = ui.text_edit_singleline(current_text);
if response.lost_focus()
&& ui.input(|input| input.key_pressed(egui::Key::Enter))
{
confirmed = true;
should_close = true;
}
ui.add_space(8.0);
}
}
ui.horizontal(|ui| {
if ui.button(&modal_data.confirm_text).clicked() {
confirmed = true;
should_close = true;
}
if ui.button(&modal_data.cancel_text).clicked() {
should_close = true;
}
});
if ui.input(|input| input.key_pressed(egui::Key::Escape)) {
should_close = true;
}
});
if should_close {
let ModalData { id, kind, .. } = modal_data;
let result = if confirmed {
match kind {
ModalKind::Confirm => ModalResult::Confirmed,
ModalKind::TextInput { current_text } => {
ModalResult::TextInput(current_text)
}
}
} else {
ModalResult::Cancelled
};
self.modals.store_result(id, result);
} else {
self.modals.active = Some(modal_data);
}
}
}
}