ui/
layout.rs

1//! This module defines the building blocks for building the layout of the editor.
2//!
3//! The current implementation builds an `egui_dock` `TabViewer` and delegates the specific layout
4//! to that. We contain the different panels and how to construct them in this module.
5
6mod assets;
7mod layers;
8mod levels;
9mod settings;
10mod status_bar;
11mod toolbar;
12
13use crate::dialogs::Dialogs;
14use crate::notifications::Notifications;
15use crate::state::UiState;
16use ::assets::AssetLibrary;
17use bevy::prelude::{BevyError, ResMut};
18use bevy_egui::EguiContexts;
19use egui::{Ui, WidgetText};
20use egui_dock::{DockArea, Style, TabViewer};
21
22/// The different panels that can be shown in the editor UI.
23/// If a new panel needs to be available for the user in the UI it needs to be added here,
24/// if it needs to be shown by default, make sure to add it in [`UiState::default`] as well.
25#[derive(Debug)]
26pub enum EditorPanels {
27    /// The "main" view that shows the underlying Bevy rendered world.
28    Editor,
29    /// Shows the assets available in the currently selected libraries.
30    Assets,
31    /// Shows the layers available in the currently selected level.
32    Layers,
33    /// Shows the levels available in the currently selected project.
34    Levels,
35    /// Shows the settings related to the UI and application.
36    Settings,
37}
38
39/// Contains the data structures that are available to the [`TabViewer`] when rendering the editor layout.
40/// See [`EditorLayout::ui`] in particular.
41pub struct EditorLayout<'a> {
42    /// The notifications resource to dispatch toasts in the UI
43    pub notifications: &'a mut Notifications,
44    /// The asset library resource for querying and modifying assets.
45    pub asset_library: &'a mut AssetLibrary,
46}
47
48impl TabViewer for EditorLayout<'_> {
49    type Tab = EditorPanels;
50
51    fn title(&mut self, tab: &mut Self::Tab) -> WidgetText {
52        format!("{tab:?}").into()
53    }
54
55    fn ui(&mut self, ui: &mut Ui, tab: &mut Self::Tab) {
56        match tab {
57            EditorPanels::Editor => {
58                // we don't render anything in the editor view.
59                // Instead, in `clear_background` we make the background transparent so the underlying
60                // Bevy render is visible.
61
62                // Later on, we'd want to get the rectangle that this pane is shown in and then update
63                // the Bevy camera to only render to this. That would prevent the camera shifting around
64                // when we move the pane.
65            }
66            EditorPanels::Assets => assets::render(self, ui),
67            EditorPanels::Layers => layers::render(self, ui),
68            EditorPanels::Levels => levels::render(self, ui),
69            EditorPanels::Settings => settings::render(self, ui),
70        }
71    }
72
73    fn closeable(&mut self, _tab: &mut Self::Tab) -> bool {
74        false
75    }
76
77    fn allowed_in_windows(&self, _tab: &mut Self::Tab) -> bool {
78        false
79    }
80
81    fn clear_background(&self, tab: &Self::Tab) -> bool {
82        !matches!(tab, EditorPanels::Editor)
83    }
84}
85
86/// Handles rendering the [`EditorLayout`] in the `World`.
87#[utils::bevy_system]
88pub fn render_editor_layout(
89    mut contexts: EguiContexts,
90    mut notifications: ResMut<Notifications>,
91    mut asset_library: ResMut<AssetLibrary>,
92    mut dialogs: ResMut<Dialogs>,
93    mut state: ResMut<UiState>,
94) -> Result<(), BevyError> {
95    let context = contexts.ctx_mut()?;
96
97    // Render any pending notifications
98    notifications.ui(context);
99
100    // Render any dialogs that are open
101    dialogs.render(context);
102
103    toolbar::render(context, dialogs.as_mut());
104    status_bar::render(context);
105
106    // construct an `EditorLayout` using our mutable world reference for rendering.
107    // the `EditorLayout` struct has a strict lifetime bound to this scope and may not leak.
108    let mut viewer = EditorLayout {
109        notifications: notifications.as_mut(),
110        asset_library: asset_library.as_mut(),
111    };
112
113    // Render the `dock_state` in the `UiState` in a DockArea.
114    DockArea::new(&mut state.dock_state)
115        .style(Style::from_egui(context.style().as_ref()))
116        .show(context, &mut viewer);
117
118    Ok(())
119}