nightshade-editor 0.13.4

An interactive editor for the Nightshade game engine
use crate::mosaic::WidgetContext;
use nightshade::ecs::generational_registry::registry_entry_by_name_mut;
use nightshade::ecs::material::resources::material_registry_insert;
use nightshade::prelude::serde::{Deserialize, Serialize};
use nightshade::prelude::*;

use crate::app_context::AppContext;
use crate::engine_editor::inspector::helpers::material_properties_ui;
use crate::messages::EditorMessage;

fn default_true() -> bool {
    true
}

#[derive(Clone, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct MaterialsWidget {
    pub selected_material: Option<String>,
    #[serde(skip)]
    pub new_material_name: String,
    #[serde(default = "default_true")]
    pub materials_list_expanded: bool,
}

impl Default for MaterialsWidget {
    fn default() -> Self {
        Self {
            selected_material: None,
            new_material_name: String::new(),
            materials_list_expanded: true,
        }
    }
}

impl MaterialsWidget {
    pub(crate) fn render(
        &mut self,
        ui: &mut egui::Ui,
        context: &mut WidgetContext<AppContext, EditorMessage>,
    ) {
        let tile_id = context.current_tile_id;
        let world = context.world_mut();

        let available_rect = ui.available_rect_before_wrap();
        ui.painter()
            .rect_filled(available_rect, 0.0, ui.style().visuals.panel_fill);

        egui::ScrollArea::vertical()
            .id_salt(("materials_widget_scroll", tile_id))
            .auto_shrink([false, false])
            .show(ui, |ui| {
                ui.set_min_width(available_rect.width());
                ui.add_space(4.0);
                ui.horizontal(|ui| {
                    ui.label("New material:");
                    ui.text_edit_singleline(&mut self.new_material_name);
                    if ui.button("Create").clicked()
                        && !self.new_material_name.is_empty()
                        && !world
                            .resources
                            .material_registry
                            .registry
                            .name_to_index
                            .contains_key(&self.new_material_name)
                    {
                        material_registry_insert(
                            &mut world.resources.material_registry,
                            self.new_material_name.clone(),
                            Material::default(),
                        );
                        self.selected_material = Some(self.new_material_name.clone());
                        self.new_material_name.clear();
                    }
                });

                ui.separator();

                let material_count = world
                    .resources
                    .material_registry
                    .registry
                    .index_to_name
                    .iter()
                    .filter(|opt| opt.is_some())
                    .count();

                let mut slot_to_remove: Option<usize> = None;
                let header_response =
                    ui.collapsing(format!("Materials ({})", material_count), |ui| {
                        egui::ScrollArea::vertical()
                            .id_salt(tile_id)
                            .max_height(200.0)
                            .show(ui, |ui| {
                                let registry = &world.resources.material_registry.registry;
                                for slot_index in 0..registry.index_to_name.len() {
                                    let Some(name) = &registry.index_to_name[slot_index] else {
                                        continue;
                                    };
                                    let ref_count = registry.reference_counts[slot_index];
                                    let is_selected =
                                        self.selected_material.as_deref() == Some(name.as_str());

                                    ui.horizontal(|ui| {
                                        let label = format!("{} ({})", name, ref_count);
                                        if ui.selectable_label(is_selected, &label).clicked() {
                                            self.selected_material = Some(name.clone());
                                        }

                                        if ref_count == 0
                                            && ui
                                                .small_button("\u{1f5d1}")
                                                .on_hover_text("Delete")
                                                .clicked()
                                        {
                                            if is_selected {
                                                self.selected_material = None;
                                            }
                                            slot_to_remove = Some(slot_index);
                                        }
                                    });
                                }
                            });
                    });

                if let Some(slot_index) = slot_to_remove {
                    let generation =
                        world.resources.material_registry.registry.generations[slot_index];
                    world
                        .resources
                        .material_registry
                        .registry
                        .remove(slot_index as u32, generation);
                }

                self.materials_list_expanded = header_response.fully_open();

                ui.separator();

                if let Some(material_name) = self.selected_material.clone() {
                    if let Some(material) = registry_entry_by_name_mut(
                        &mut world.resources.material_registry.registry,
                        &material_name,
                    ) {
                        ui.collapsing(format!("Edit: {}", material_name), |ui| {
                            material_properties_ui(ui, material, &format!("widget_{:?}", tile_id));

                            ui.separator();

                            ui.label("Textures:");

                            let mut base_texture_str =
                                material.base_texture.clone().unwrap_or_default();
                            ui.horizontal(|ui| {
                                ui.label("Base Texture:");
                                if ui.text_edit_singleline(&mut base_texture_str).changed() {
                                    material.base_texture = if base_texture_str.is_empty() {
                                        None
                                    } else {
                                        Some(base_texture_str)
                                    };
                                }
                            });

                            let mut emissive_texture_str =
                                material.emissive_texture.clone().unwrap_or_default();
                            ui.horizontal(|ui| {
                                ui.label("Emissive Texture:");
                                if ui.text_edit_singleline(&mut emissive_texture_str).changed() {
                                    material.emissive_texture = if emissive_texture_str.is_empty() {
                                        None
                                    } else {
                                        Some(emissive_texture_str)
                                    };
                                }
                            });
                        });
                    } else {
                        self.selected_material = None;
                    }
                }
            });
    }
}