Skip to main content

bevy_yoleck/
editor_window.rs

1use bevy::prelude::*;
2use bevy::window::PrimaryWindow;
3use bevy_egui::{EguiContext, PrimaryEguiContext, egui};
4
5use crate::YoleckEditorBottomPanelSections;
6use crate::YoleckEditorLeftPanelSections;
7use crate::YoleckEditorRightPanelSections;
8use crate::YoleckEditorTopPanelSections;
9use crate::editor_panels::EditorPanel;
10
11#[derive(Resource, Default)]
12pub struct YoleckEditorViewportRect {
13    pub rect: Option<egui::Rect>,
14}
15
16pub(crate) fn yoleck_editor_window(
17    world: &mut World,
18    mut egui_query: Local<Option<QueryState<&mut EguiContext, With<PrimaryEguiContext>>>>,
19) {
20    let egui_query = egui_query.get_or_insert_with(|| world.query_filtered());
21    let mut borrowed_egui = if let Ok(mut egui_context) = egui_query.single_mut(world) {
22        core::mem::take(egui_context.as_mut())
23    } else {
24        return;
25    };
26
27    let ctx = borrowed_egui.get_mut();
28
29    // The order of panels is important, because panels that go first will take height/width from
30    // adjacent panels. The top panel must go first because it's height is very small (so it won't
31    // be impacting the side panels much) and it needs all the width it can get. The bottom panel
32    // can go last because giving the side panels more height for displaying their lists is more
33    // important than giving extra width to the console.
34
35    let mut viewport_ui = egui::Ui::new(
36        ctx.clone(),
37        "viewport".into(),
38        egui::UiBuilder::new()
39            .layer_id(egui::LayerId::background())
40            .max_rect(ctx.viewport_rect()),
41    );
42
43    let top = YoleckEditorTopPanelSections::show_panel(world, ctx, &mut viewport_ui)
44        .rect
45        .height();
46
47    let left = YoleckEditorLeftPanelSections::show_panel(world, ctx, &mut viewport_ui)
48        .rect
49        .width();
50
51    let right = YoleckEditorRightPanelSections::show_panel(world, ctx, &mut viewport_ui)
52        .rect
53        .width();
54
55    let bottom = YoleckEditorBottomPanelSections::show_panel(world, ctx, &mut viewport_ui)
56        .rect
57        .height();
58
59    let viewport_rect = egui::Rect::from_min_max(
60        egui::Pos2::new(left, top),
61        egui::Pos2::new(
62            ctx.input(|i| i.viewport_rect().width()) - right,
63            ctx.input(|i| i.viewport_rect().height()) - bottom,
64        ),
65    );
66
67    if let Some(mut editor_viewport) = world.get_resource_mut::<YoleckEditorViewportRect>() {
68        editor_viewport.rect = Some(viewport_rect);
69    }
70
71    if let Ok(window) = world
72        .query_filtered::<&bevy::window::Window, With<PrimaryWindow>>()
73        .single(world)
74    {
75        let scale = window.scale_factor();
76
77        let left_px = (left * scale) as u32;
78        let right_px = (right * scale) as u32;
79        let top_px = (top * scale) as u32;
80        let bottom_px = (bottom * scale) as u32;
81
82        let pos = UVec2::new(left_px, top_px);
83        let size = UVec2::new(window.physical_width(), window.physical_height())
84            .saturating_sub(pos)
85            .saturating_sub(UVec2::new(right_px, bottom_px));
86
87        if size.x > 0 && size.y > 0 {
88            let mut camera_query =
89                world.query_filtered::<&mut Camera, Without<PrimaryEguiContext>>();
90            for mut camera in camera_query.iter_mut(world) {
91                camera.viewport = Some(bevy::camera::Viewport {
92                    physical_position: pos,
93                    physical_size: size,
94                    ..default()
95                });
96            }
97        }
98    }
99
100    if let Ok(mut egui_context) = egui_query.single_mut(world) {
101        *egui_context = borrowed_egui;
102    }
103}