use bevy::prelude::*;
use bevy_egui::{egui, EguiContext, EguiPlugin, EguiSettings};
const BEVY_TEXTURE_ID: u64 = 0;
fn main() {
App::build()
.insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0)))
.insert_resource(Msaa { samples: 4 })
.init_resource::<UiState>()
.add_plugins(DefaultPlugins)
.add_plugin(EguiPlugin)
.add_startup_system(load_assets.system())
.add_system(update_ui_scale_factor.system())
.add_system(ui_example.system())
.run();
}
#[derive(Default)]
struct UiState {
label: String,
value: f32,
painting: Painting,
inverted: bool,
}
fn load_assets(mut egui_context: ResMut<EguiContext>, assets: Res<AssetServer>) {
let texture_handle = assets.load("icon.png");
egui_context.set_egui_texture(BEVY_TEXTURE_ID, texture_handle);
}
pub fn update_ui_scale_factor(
keyboard_input: Res<Input<KeyCode>>,
mut toggle_scale_factor: Local<Option<bool>>,
mut egui_settings: ResMut<EguiSettings>,
windows: Res<Windows>,
) {
if keyboard_input.just_pressed(KeyCode::Slash) || toggle_scale_factor.is_none() {
*toggle_scale_factor = Some(!toggle_scale_factor.unwrap_or(true));
if let Some(window) = windows.get_primary() {
let scale_factor = if toggle_scale_factor.unwrap() {
1.0
} else {
1.0 / window.scale_factor()
};
egui_settings.scale_factor = scale_factor;
}
}
}
fn ui_example(
mut egui_ctx: ResMut<EguiContext>,
mut ui_state: ResMut<UiState>,
assets: Res<AssetServer>,
) {
let mut load = false;
let mut remove = false;
let mut invert = false;
egui::SidePanel::left("side_panel")
.default_width(200.0)
.show(egui_ctx.ctx(), |ui| {
ui.heading("Side Panel");
ui.horizontal(|ui| {
ui.label("Write something: ");
ui.text_edit_singleline(&mut ui_state.label);
});
ui.add(egui::Slider::new(&mut ui_state.value, 0.0..=10.0).text("value"));
if ui.button("Increment").clicked() {
ui_state.value += 1.0;
}
ui.allocate_space(egui::Vec2::new(1.0, 100.0));
ui.horizontal(|ui| {
load = ui.button("Load").clicked();
invert = ui.button("Invert").clicked();
remove = ui.button("Remove").clicked();
});
ui.add(egui::widgets::Image::new(
egui::TextureId::User(BEVY_TEXTURE_ID),
[256.0, 256.0],
));
ui.with_layout(egui::Layout::bottom_up(egui::Align::Center), |ui| {
ui.add(
egui::Hyperlink::new("https://github.com/emilk/egui/").text("powered by egui"),
);
});
});
egui::TopBottomPanel::top("top_panel").show(egui_ctx.ctx(), |ui| {
egui::menu::bar(ui, |ui| {
egui::menu::menu(ui, "File", |ui| {
if ui.button("Quit").clicked() {
std::process::exit(0);
}
});
});
});
egui::CentralPanel::default().show(egui_ctx.ctx(), |ui| {
ui.heading("Egui Template");
ui.hyperlink("https://github.com/emilk/egui_template");
ui.add(egui::github_link_file_line!(
"https://github.com/emilk/egui_template/blob/master/",
"Direct link to source code."
));
egui::warn_if_debug_build(ui);
ui.separator();
ui.heading("Central Panel");
ui.label("The central panel the region left after adding TopPanel's and SidePanel's");
ui.label("It is often a great place for big things, like drawings:");
ui.heading("Draw with your mouse to paint:");
ui_state.painting.ui_control(ui);
egui::Frame::dark_canvas(ui.style()).show(ui, |ui| {
ui_state.painting.ui_content(ui);
});
});
egui::Window::new("Window")
.scroll(true)
.show(egui_ctx.ctx(), |ui| {
ui.label("Windows can be moved by dragging them.");
ui.label("They are automatically sized based on contents.");
ui.label("You can turn on resizing and scrolling if you like.");
ui.label("You would normally chose either panels OR windows.");
});
if invert {
ui_state.inverted = !ui_state.inverted;
}
if load || invert {
let texture_handle = if ui_state.inverted {
assets.load("icon_inverted.png")
} else {
assets.load("icon.png")
};
egui_ctx.set_egui_texture(BEVY_TEXTURE_ID, texture_handle);
}
if remove {
egui_ctx.remove_egui_texture(BEVY_TEXTURE_ID);
}
}
struct Painting {
lines: Vec<Vec<egui::Vec2>>,
stroke: egui::Stroke,
}
impl Default for Painting {
fn default() -> Self {
Self {
lines: Default::default(),
stroke: egui::Stroke::new(1.0, egui::Color32::LIGHT_BLUE),
}
}
}
impl Painting {
pub fn ui_control(&mut self, ui: &mut egui::Ui) -> egui::Response {
ui.horizontal(|ui| {
egui::stroke_ui(ui, &mut self.stroke, "Stroke");
ui.separator();
if ui.button("Clear Painting").clicked() {
self.lines.clear();
}
})
.response
}
pub fn ui_content(&mut self, ui: &mut egui::Ui) {
let (response, painter) =
ui.allocate_painter(ui.available_size_before_wrap_finite(), egui::Sense::drag());
let rect = response.rect;
if self.lines.is_empty() {
self.lines.push(vec![]);
}
let current_line = self.lines.last_mut().unwrap();
if let Some(pointer_pos) = response.interact_pointer_pos() {
let canvas_pos = pointer_pos - rect.min;
if current_line.last() != Some(&canvas_pos) {
current_line.push(canvas_pos);
}
} else if !current_line.is_empty() {
self.lines.push(vec![]);
}
for line in &self.lines {
if line.len() >= 2 {
let points: Vec<egui::Pos2> = line.iter().map(|p| rect.min + *p).collect();
painter.add(egui::Shape::line(points, self.stroke));
}
}
}
}