//! Top editor toolbar (below menu bar): spawn/file left, transport center. use bevy::prelude::*; use bevy_egui::egui; use egui_phosphor_icons::icons; use crate::history::spawn_with_history; use crate::scene_io::{SceneIo, SceneIoRequest}; use crate::state::{EditorMode, PlayPaused, PlayPossession}; use crate::ui::UiState; use crate::viewport::brush_tool::start_draw_brush_tool; use crate::viewport::{editor_spawn_ground_position, snap_translation, ViewportSettings}; use shared::PrimitiveShape; use super::helpers::{ light_snapshot, primitive_snapshot, toggle_play_mode, toggle_play_paused, toggle_possession, }; use super::widgets::{icon_button, panel_toolbar_row, toolbar_separator, transport_button_large}; /// Toolbar strip height (menu bar is separate). const TOOLBAR_HEIGHT: f32 = 56.0; const TRANSPORT_WIDTH: f32 = 152.0; const TRANSPORT_SLOT_STEP: f32 = 54.0; pub fn editor_toolbar_panel(world: &mut World, root_ui: &mut egui::Ui) { egui::Panel::top("editor_toolbar") .exact_size(TOOLBAR_HEIGHT) .show_inside(root_ui, |ui| { toolbar_ui(world, ui); }); } pub fn toolbar_ui(world: &mut World, ui: &mut egui::Ui) { let (toolbar_rect, _) = ui.allocate_exact_size( egui::vec2(ui.available_width(), TOOLBAR_HEIGHT - 4.0), egui::Sense::hover(), ); let center_y = toolbar_rect.center().y; let left_rect = egui::Rect::from_min_max( egui::pos2(toolbar_rect.left() + 8.0, center_y - 18.0), egui::pos2(toolbar_rect.right(), center_y + 18.0), ); let transport_rect = egui::Rect::from_center_size( egui::pos2(toolbar_rect.center().x, center_y), egui::vec2(TRANSPORT_WIDTH, 44.0), ); let mut left_ui = ui.new_child( egui::UiBuilder::new() .id_salt("toolbar_left") .max_rect(left_rect) .layout(egui::Layout::left_to_right(egui::Align::Center)), ); left_ui.spacing_mut().item_spacing.x = 6.0; left_toolbar(world, &mut left_ui); let mut transport_ui = ui.new_child( egui::UiBuilder::new() .id_salt("toolbar_transport") .max_rect(transport_rect) .layout(egui::Layout::left_to_right(egui::Align::Center)), ); transport_ui.spacing_mut().item_spacing.x = 10.0; transport_controls(world, &mut transport_ui); } fn left_toolbar(world: &mut World, ui: &mut egui::Ui) { panel_toolbar_row(ui, |ui| { if icon_button(ui, icons::CUBE, "Spawn cube").clicked() { spawn_primitive_at_camera(world, PrimitiveShape::Box, "Cube"); } if icon_button(ui, icons::MOUNTAINS, "Spawn ramp").clicked() { spawn_primitive_at_camera(world, PrimitiveShape::Ramp, "Ramp"); } if icon_button(ui, icons::SPHERE, "Spawn sphere").clicked() { spawn_primitive_at_camera(world, PrimitiveShape::Sphere, "Sphere"); } if icon_button(ui, icons::LIGHTBULB, "Spawn point light").clicked() { let pos = camera_ground_spawn(world); spawn_with_history(world, light_snapshot("Point Light", pos)); } if icon_button(ui, icons::PENCIL_SIMPLE_LINE, "Draw brush (B)").clicked() { start_draw_brush_tool(world); } toolbar_separator(ui); if icon_button(ui, icons::FILE, "New scene").clicked() { world.resource_mut::().request = Some(SceneIoRequest::New); } if icon_button(ui, icons::FOLDER_OPEN, "Open scene").clicked() { world.resource_mut::().request = Some(SceneIoRequest::Open); } if icon_button(ui, icons::FLOPPY_DISK, "Save scene").clicked() { world.resource_mut::().request = Some(SceneIoRequest::Save); } if icon_button(ui, icons::COPY, "Save scene as").clicked() { world.resource_mut::().request = Some(SceneIoRequest::SaveAs); } }); } fn transport_controls(world: &mut World, ui: &mut egui::Ui) { let mode = *world.resource::>().get(); match mode { EditorMode::Editing => { ui.add_space(TRANSPORT_SLOT_STEP); let play_clicked = ui .push_id("transport_center", |ui| { transport_button_large(ui, icons::PLAY, "Play (F5)", true).clicked() }) .inner; if play_clicked { toggle_play_mode(world); } } EditorMode::Playing => { let paused = world.resource::().0; let pause_icon = if paused { icons::PLAY } else { icons::PAUSE }; let pause_tip = if paused { "Resume simulation (F6)" } else { "Pause simulation (F6)" }; let pause_clicked = ui .push_id("transport_left", |ui| { transport_button_large(ui, pause_icon, pause_tip, false).clicked() }) .inner; if pause_clicked { toggle_play_paused(world); } let stop_clicked = ui .push_id("transport_center", |ui| { transport_button_large(ui, icons::STOP, "Stop and return to Edit (F5)", false) .clicked() }) .inner; if stop_clicked { toggle_play_mode(world); } let possession = *world.resource::(); let (eject_icon, eject_tip) = match possession { PlayPossession::Possessed => (icons::SIGN_OUT, "Eject from player (F8)"), PlayPossession::Ejected => (icons::USER, "Possess player (F8)"), }; let possession_clicked = ui .push_id("transport_right", |ui| { transport_button_large(ui, eject_icon, eject_tip, false).clicked() }) .inner; if possession_clicked { toggle_possession(world); } } } } fn camera_ground_spawn(world: &mut World) -> Vec3 { let rect = world.resource::().viewport_rect; let settings = world.resource::().clone(); let pos = editor_spawn_ground_position(world, rect); snap_translation(pos, &settings) } fn spawn_primitive_at_camera(world: &mut World, shape: PrimitiveShape, name: &str) { let pos = camera_ground_spawn(world); spawn_with_history(world, primitive_snapshot(name, shape, pos)); }