1use bevy::prelude::*;
2use bevy::utils::HashSet;
3use bevy::window::PrimaryWindow;
4use bevy_egui::egui::{Ui, WidgetText};
5use bevy_egui::{EguiContext, EguiPlugin, EguiSet};
6use egui_dock::{DockArea, DockState, NodeIndex, Style, Tree};
7
8use crate::systems::{draw_inspected_data, draw_options};
9
10pub struct GuiPlugin;
14
15impl Plugin for GuiPlugin {
16 fn build(&self, app: &mut App) {
17 app.add_plugins(EguiPlugin)
18 .insert_resource(UiState::default())
19 .add_systems(
20 PostUpdate,
21 show_ui
22 .before(EguiSet::ProcessOutput)
23 .before(TransformSystem::TransformPropagate),
24 );
25 }
26}
27
28fn show_ui(world: &mut World) {
31 let Ok(egui_context) = world
32 .query_filtered::<&mut EguiContext, With<PrimaryWindow>>()
33 .get_single(world)
34 else {
35 return;
36 };
37
38 let mut egui_context = egui_context.clone();
39
40 world.resource_scope::<UiState, _>(|world, mut ui_state| {
41 ui_state.ui(world, egui_context.get_mut());
42 });
43}
44
45#[derive(Debug, PartialEq, Eq)]
48pub enum CustomTab {
49 Render,
50 Inspector,
51 Options,
52}
53
54#[derive(Resource)]
55pub struct UiState {
56 state: DockState<CustomTab>,
57 viewport_rect: bevy_egui::egui::Rect,
58 pub(crate) selected_entities: HashSet<Entity>,
59}
60
61impl Default for UiState {
62 fn default() -> Self {
63 let mut state = DockState::new(vec![CustomTab::Render]);
64 let tree = state.main_surface_mut();
65 let [_render, options_and_inspector] =
66 tree.split_left(NodeIndex::root(), 0.3, vec![CustomTab::Options]);
67 let [_options, _inspector] =
68 tree.split_below(options_and_inspector, 0.5, vec![CustomTab::Inspector]);
69
70 Self {
71 state,
72 viewport_rect: egui_dock::egui::Rect::NOTHING,
73 selected_entities: HashSet::new(),
74 }
75 }
76}
77
78impl UiState {
79 pub fn ui(&mut self, world: &mut World, ctx: &mut bevy_egui::egui::Context) {
80 let mut tab_viewer = TabViewer {
81 world,
82 viewport_rect: &mut self.viewport_rect,
83 selected_entities: &mut self.selected_entities,
84 };
85 DockArea::new(&mut self.state)
86 .style(Style::from_egui(ctx.style().as_ref()))
87 .show(ctx, &mut tab_viewer);
88 }
89
90 pub fn tree(&self) -> &Tree<CustomTab> {
91 self.state.main_surface()
92 }
93
94 pub fn tree_mut(&mut self) -> &mut Tree<CustomTab> {
95 self.state.main_surface_mut()
96 }
97}
98
99pub struct TabViewer<'a> {
100 world: &'a mut World,
101 selected_entities: &'a mut HashSet<Entity>,
102 viewport_rect: &'a mut bevy_egui::egui::Rect,
103}
104
105impl egui_dock::TabViewer for TabViewer<'_> {
106 type Tab = CustomTab;
107
108 fn title(&mut self, tab: &mut Self::Tab) -> WidgetText {
109 format!("{tab:?}").into()
110 }
111
112 fn ui(&mut self, ui: &mut Ui, tab: &mut Self::Tab) {
113 match tab {
114 CustomTab::Render => *self.viewport_rect = ui.clip_rect(),
115 CustomTab::Inspector => draw_inspected_data(ui, self.world, self.selected_entities),
116 CustomTab::Options => draw_options(ui, self.world),
117 }
118 }
119
120 fn clear_background(&self, tab: &Self::Tab) -> bool {
121 !matches!(tab, CustomTab::Render)
122 }
123}