nightshade-editor 0.13.4

An interactive editor for the Nightshade game engine
use crate::mosaic::WidgetContext;
use nightshade::prelude::serde::{Deserialize, Serialize};
use nightshade::prelude::*;

use crate::app_context::AppContext;
use crate::messages::EditorMessage;

#[derive(Clone, Default, Serialize, Deserialize)]
#[serde(crate = "nightshade::prelude::serde")]
pub struct CameraWidget {
    pub camera_index: usize,
}

impl CameraWidget {
    pub(crate) fn render(
        &mut self,
        ui: &mut egui::Ui,
        context: &mut WidgetContext<AppContext, EditorMessage>,
    ) {
        let rect = ui.available_rect_before_wrap();
        let tile_id = context.current_tile_id;
        let pixels_per_point = ui.ctx().pixels_per_point();

        let cameras = context.cached_cameras;
        if !cameras.is_empty() && self.camera_index >= cameras.len() {
            self.camera_index = cameras.len() - 1;
        }
        let camera_entity = cameras.get(self.camera_index).copied();

        let screen_rect = ui.ctx().content_rect();
        let (viewport_width, viewport_height) = if let Some((physical_width, physical_height)) =
            context.world().resources.window.cached_viewport_size
        {
            let scale_x = physical_width as f32 / screen_rect.width();
            let scale_y = physical_height as f32 / screen_rect.height();
            (
                (rect.width() * scale_x).round() as u32,
                (rect.height() * scale_y).round() as u32,
            )
        } else {
            (
                (rect.width() * pixels_per_point).round() as u32,
                (rect.height() * pixels_per_point).round() as u32,
            )
        };

        let texture_index = camera_entity.and_then(|cam| {
            context
                .world()
                .resources
                .user_interface
                .required_cameras
                .iter()
                .position(|&entity| entity == cam)
        });

        if let Some(index) = texture_index {
            context
                .world_mut()
                .resources
                .user_interface
                .required_camera_sizes[index] = (viewport_width, viewport_height);
        }

        let clicked = if let Some(index) = texture_index
            && let Some(texture_id) = context.viewport_textures.get(index)
        {
            let image = egui::Image::new(egui::load::SizedTexture::new(
                *texture_id,
                egui::vec2(rect.width(), rect.height()),
            ))
            .fit_to_exact_size(egui::vec2(rect.width(), rect.height()))
            .sense(egui::Sense::click());

            let response = ui.put(rect, image);
            response.clicked()
        } else {
            let response = ui.allocate_rect(rect, egui::Sense::click());
            response.clicked()
        };

        let was_none = context.selected_viewport_tile.is_none();
        if was_none {
            *context.selected_viewport_tile = Some(tile_id);
        }
        if clicked {
            *context.selected_viewport_tile = Some(tile_id);
        }
        let is_selected = *context.selected_viewport_tile == Some(tile_id);

        let world = context.world_mut();
        if (was_none || clicked || is_selected)
            && let Some(cam) = camera_entity
        {
            world.resources.active_camera = Some(cam);
        }
        if is_selected {
            world.resources.window.active_viewport_rect = Some(nightshade::prelude::ViewportRect {
                x: rect.min.x * pixels_per_point,
                y: rect.min.y * pixels_per_point,
                width: rect.width() * pixels_per_point,
                height: rect.height() * pixels_per_point,
            });
        }

        if is_selected {
            ui.painter().rect_stroke(
                rect,
                egui::CornerRadius::ZERO,
                egui::Stroke::new(3.0, egui::Color32::from_rgb(255, 165, 0)),
                egui::StrokeKind::Inside,
            );
        }

        if cameras.len() > 1 {
            let selector_width = 120.0;
            let selector_height = 24.0;
            let margin = 8.0;
            let selector_rect = egui::Rect::from_min_size(
                egui::pos2(rect.min.x + margin, rect.min.y + margin),
                egui::vec2(selector_width, selector_height),
            );

            let mut child_ui = ui.new_child(
                egui::UiBuilder::new()
                    .max_rect(selector_rect)
                    .layout(egui::Layout::left_to_right(egui::Align::Center)),
            );

            let world = context.world();
            let current_camera_name = camera_entity
                .and_then(|entity| world.core.get_name(entity))
                .map(|name| name.0.clone())
                .unwrap_or_else(|| format!("Camera {}", self.camera_index + 1));

            egui::ComboBox::from_id_salt(("camera_selector", tile_id))
                .selected_text(&current_camera_name)
                .width(selector_width - 8.0)
                .show_ui(&mut child_ui, |ui| {
                    for (index, &entity) in cameras.iter().enumerate() {
                        let name = world
                            .core
                            .get_name(entity)
                            .map(|name| name.0.clone())
                            .unwrap_or_else(|| format!("Camera {}", index + 1));

                        if ui
                            .selectable_label(self.camera_index == index, &name)
                            .clicked()
                        {
                            self.camera_index = index;
                        }
                    }
                });
        }
    }
}