1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
use bevy::{
camera::{CameraOutputMode, Viewport, visibility::RenderLayers},
prelude::*,
window::PrimaryWindow,
};
use bevy_egui::{
EguiContext, EguiContexts, EguiGlobalSettings, EguiPlugin, EguiPrimaryContextPass,
PrimaryEguiContext, egui,
};
use wgpu_types::BlendState;
fn main() {
App::new()
.insert_resource(ClearColor(Color::srgb(0.25, 0.25, 0.25)))
.add_plugins(DefaultPlugins)
.add_plugins(EguiPlugin::default())
.add_systems(Startup, setup_system)
.add_systems(EguiPrimaryContextPass, ui_example_system)
.run();
}
// This function runs every frame. Therefore, updating the viewport after drawing the gui.
// With a resource which stores the dimensions of the panels, the update of the Viewport can
// be done in another system.
fn ui_example_system(
mut contexts: EguiContexts,
mut camera: Single<&mut Camera, Without<EguiContext>>,
window: Single<&mut Window, With<PrimaryWindow>>,
) -> Result {
let ctx = contexts.ctx_mut()?;
let mut left = egui::SidePanel::left("left_panel")
.resizable(true)
.show(ctx, |ui| {
ui.label("Left resizeable panel");
ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover());
})
.response
.rect
.width(); // height is ignored, as the panel has a hight of 100% of the screen
let mut right = egui::SidePanel::right("right_panel")
.resizable(true)
.show(ctx, |ui| {
ui.label("Right resizeable panel");
ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover());
})
.response
.rect
.width(); // height is ignored, as the panel has a height of 100% of the screen
let mut top = egui::TopBottomPanel::top("top_panel")
.resizable(true)
.show(ctx, |ui| {
ui.label("Top resizeable panel");
ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover());
})
.response
.rect
.height(); // width is ignored, as the panel has a width of 100% of the screen
let mut bottom = egui::TopBottomPanel::bottom("bottom_panel")
.resizable(true)
.show(ctx, |ui| {
ui.label("Bottom resizeable panel");
ui.allocate_rect(ui.available_rect_before_wrap(), egui::Sense::hover());
})
.response
.rect
.height(); // width is ignored, as the panel has a width of 100% of the screen
// Scale from logical units to physical units.
left *= window.scale_factor();
right *= window.scale_factor();
top *= window.scale_factor();
bottom *= window.scale_factor();
// -------------------------------------------------
// | left | top ^^^^^^ | right |
// | panel | panel height | panel |
// | | vvvvvv | |
// | |---------------------------| |
// | | | |
// |<-width->| viewport |<-width->|
// | | | |
// | |---------------------------| |
// | | bottom ^^^^^^ | |
// | | panel height | |
// | | vvvvvv | |
// -------------------------------------------------
//
// The upper left point of the viewport is the width of the left panel and the height of the
// top panel
//
// The width of the viewport the width of the top/bottom panel
// Alternative the width can be calculated as follow:
// size.x = window width - left panel width - right panel width
//
// The height of the viewport is:
// size.y = window height - top panel height - bottom panel height
//
// Therefore we use the alternative for the width, as we can callculate the Viewport as
// following:
let pos = UVec2::new(left as u32, top as u32);
let size = UVec2::new(window.physical_width(), window.physical_height())
- pos
- UVec2::new(right as u32, bottom as u32);
camera.viewport = Some(Viewport {
physical_position: pos,
physical_size: size,
..default()
});
Ok(())
}
// Set up the example entities for the scene. The only important thing is a camera which
// renders directly to the window.
fn setup_system(
mut commands: Commands,
mut egui_global_settings: ResMut<EguiGlobalSettings>,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<ColorMaterial>>,
) {
// Disable the automatic creation of a primary context to set it up manually for the camera we need.
egui_global_settings.auto_create_primary_context = false;
// Circle.
commands.spawn((
Mesh2d(meshes.add(Circle::new(50.))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(Color::srgb(0.2, 0.1, 0.0)))),
Transform::from_translation(Vec3::new(-150., 0., 0.)),
));
// Rectangles.
commands.spawn((
Mesh2d(meshes.add(Rectangle::new(50., 100.))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(Color::srgb(0.5, 0.4, 0.3)))),
Transform::from_translation(Vec3::new(-50., 0., 0.)),
));
commands.spawn((
Mesh2d(meshes.add(Rectangle::new(50., 100.))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(Color::srgb(0.5, 0.4, 0.3)))),
Transform::from_translation(Vec3::new(50., 0., 0.)),
));
// Hexagon.
commands.spawn((
Mesh2d(meshes.add(RegularPolygon::new(50., 6))),
MeshMaterial2d(materials.add(ColorMaterial::from_color(Color::srgb(0.8, 0.7, 0.6)))),
Transform::from_translation(Vec3::new(150., 0., 0.)),
));
// World camera.
commands.spawn(Camera2d);
// Egui camera.
commands.spawn((
// The `PrimaryEguiContext` component requires everything needed to render a primary context.
PrimaryEguiContext,
Camera2d,
// Setting RenderLayers to none makes sure we won't render anything apart from the UI.
RenderLayers::none(),
Camera {
order: 1,
output_mode: CameraOutputMode::Write {
blend_state: Some(BlendState::ALPHA_BLENDING),
clear_color: ClearColorConfig::None,
},
clear_color: ClearColorConfig::Custom(Color::NONE),
..default()
},
));
}