Use bevy schedule for all UI

This commit is contained in:
Alex Mikhalev 2023-05-02 21:28:37 -07:00
parent d744525903
commit 25c03beb2d

View File

@ -5,7 +5,7 @@ use bevy_ecs::{
system::{Commands, IntoSystem, Query, ReadOnlySystem, Res, ResMut, System},
};
use eframe::{
egui::{self, CursorIcon, Painter, Response, Sense, Ui},
egui::{self, CursorIcon, Painter, Response, Sense},
emath::{Pos2, Rect, RectTransform, Vec2},
epaint::{Color32, Hsva, Stroke},
};
@ -21,7 +21,8 @@ fn main() {
"sketchrs",
options,
Box::new(|_cc| Box::new(MyApp::default())),
).unwrap();
)
.unwrap();
}
#[derive(Clone, Copy, PartialEq, Resource)]
@ -390,12 +391,6 @@ fn paint_points(
});
}
struct MyApp {
world: World,
show_entities_stage: Schedule,
show_sidebar_stage: Schedule,
}
fn init(mut commands: Commands) {
let p1 = geometry::insert_point_at(&mut commands, (10., 30.));
let p2 = geometry::insert_point_at(&mut commands, (-20., 15.));
@ -403,24 +398,25 @@ fn init(mut commands: Commands) {
commands.spawn(Line::new(p1, p2));
}
impl Default for MyApp {
fn default() -> Self {
let mut world = World::default();
#[derive(Resource)]
struct ContextRes(egui::Context);
world.init_resource::<Tool>();
impl Deref for ContextRes {
type Target = egui::Context;
let mut init_stage = Schedule::new();
init_stage.add_system(init);
init_stage.run(&mut world);
Self {
world,
show_entities_stage: Self::create_show_entities_stage(),
show_sidebar_stage: Self::create_show_sidebar_stage(),
}
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
#[system_set(base)]
pub enum ShowEntitiesStage {
Input,
Tools,
Paint,
}
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
#[system_set(base)]
pub enum ScheduleSet {
@ -429,8 +425,40 @@ pub enum ScheduleSet {
Paint,
}
impl MyApp {
fn create_show_entities_stage() -> Schedule {
fn prepare(ctx: Res<ContextRes>) {
ctx.request_repaint();
ctx.set_visuals(egui::Visuals::dark());
}
fn toolbar<'a>(
ctx: Res<ContextRes>,
selected: Query<Entity, With<Selected>>,
mut commands: Commands,
mut tool: ResMut<Tool>,
) {
egui::TopBottomPanel::top("top_panel").show(&**ctx, |ui| {
// ui.heading("sketchrs");
let mut selected_tool = *tool;
ui.horizontal(|ui| {
ui.radio_value(&mut selected_tool, Tool::Select, "Select");
ui.radio_value(&mut selected_tool, Tool::Move, "Move");
ui.radio_value(&mut selected_tool, Tool::AddPoint, "Add Point");
ui.radio_value(&mut selected_tool, Tool::AddLine, "Add Line");
});
if selected_tool != *tool {
*tool = selected_tool;
for selected in selected.iter() {
commands.entity(selected).remove::<Selected>();
}
}
});
}
struct ShowEntitiesSchedule(Schedule);
impl Default for ShowEntitiesSchedule {
fn default() -> Self {
let mut schedule = Schedule::new();
schedule
.configure_sets((ScheduleSet::Input, ScheduleSet::Tools, ScheduleSet::Paint).chain());
@ -442,6 +470,7 @@ impl MyApp {
move_tool.run_if(is_tool_active(Tool::Move)),
add_point_tool.run_if(is_tool_active(Tool::AddPoint)),
add_line_tool.run_if(is_tool_active(Tool::AddLine)),
add_relation_tool.run_if(is_tool_active(Tool::AddRelation)),
)
.distributive_run_if(is_hovered)
.in_base_set(ScheduleSet::Tools),
@ -449,111 +478,120 @@ impl MyApp {
schedule.add_systems(
(paint_lines, paint_points.after(paint_lines)).in_base_set(ScheduleSet::Paint),
);
schedule
Self(schedule)
}
}
fn create_show_sidebar_stage() -> Schedule {
let mut schedule = Schedule::new();
schedule
}
fn central_panel(mut world: &mut World, mut schedule: Local<ShowEntitiesSchedule>) {
let ctx = world.get_resource::<ContextRes>().unwrap().0.clone();
egui::CentralPanel::default()
.frame(egui::Frame::none())
.show(&ctx, |ui| {
let sense = match *world.resource::<Tool>() {
Tool::Move => Sense::drag(),
Tool::Select | Tool::AddPoint | Tool::AddLine | Tool::AddRelation => Sense::click(),
};
fn show_toolbar(&mut self, ui: &mut Ui) {
ui.heading("sketchrs");
let (response, painter) = ui.allocate_painter(ui.available_size(), sense);
let to_screen = RectTransform::from_to(
Rect::from_center_size(Pos2::ZERO, response.rect.size() / 2.0),
response.rect,
);
world.insert_resource(ToScreen(to_screen));
world.insert_resource(ResponseRes(response));
world.insert_resource(PainterRes(painter));
ui.horizontal(|ui| {
ui.label("Tool: ");
schedule.0.run(&mut world);
let tool = &mut *self.world.resource_mut::<Tool>();
ui.selectable_value(tool, Tool::Select, "Select");
ui.selectable_value(tool, Tool::Move, "Move");
ui.selectable_value(tool, Tool::AddPoint, "+ Point");
ui.selectable_value(tool, Tool::AddLine, "+ Line");
ui.selectable_value(tool, Tool::AddRelation, "+ Relation");
world.remove_resource::<PainterRes>();
world.remove_resource::<ResponseRes>();
world.remove_resource::<ToScreen>();
});
}
}
fn show_entities(&mut self, ui: &mut Ui) {
let sense = match *self.world.resource::<Tool>() {
Tool::Move => Sense::drag(),
Tool::Select | Tool::AddPoint | Tool::AddLine | Tool::AddRelation => Sense::click(),
};
let (response, painter) = ui.allocate_painter(ui.available_size(), sense);
let to_screen = ToScreen(RectTransform::from_to(
Rect::from_center_size(Pos2::ZERO, response.rect.size() / 2.0),
response.rect,
));
self.world.insert_resource(to_screen);
self.world.insert_resource(ResponseRes(response));
self.world.insert_resource(PainterRes(painter));
self.show_entities_stage.run(&mut self.world);
self.world.remove_resource::<PainterRes>();
self.world.remove_resource::<ResponseRes>();
self.world.remove_resource::<ToScreen>();
}
fn show_side_panel(&mut self, ui: &mut Ui) {
let tool = *self.world.resource::<Tool>();
ui.vertical(|ui| match tool {
Tool::Select => {
let mut selected = self
.world
.query_filtered::<(Entity, Option<&Point>, Option<&Line>), With<Selected>>();
let mut count = 0;
selected.for_each(&self.world, |(id, point, line)| {
count += 1;
if point.is_some() {
ui.label(format!("Selected point {}", id.index()));
} else if line.is_some() {
ui.label(format!("Selected line {}", id.index()));
fn side_panel(
ctx: Res<ContextRes>,
selected: Query<(Entity, Option<&Point>, Option<&Line>), With<Selected>>,
tool: Res<Tool>,
) {
egui::SidePanel::right("side_panel")
.resizable(true)
.default_width(150.0)
.width_range(80.0..=200.0)
.show(&**ctx, |ui| {
let tool = *tool;
ui.vertical(|ui| match tool {
Tool::Select => {
let mut count = 0;
selected.for_each(|(id, point, line)| {
count += 1;
if point.is_some() {
ui.label(format!("Selected point {}", id.index()));
} else if line.is_some() {
ui.label(format!("Selected line {}", id.index()));
}
});
if count == 0 {
ui.label("No selection");
}
});
if count == 0 {
ui.label("No selection");
}
}
Tool::AddRelation => {
let mut selected = self
.world
.query_filtered::<(Entity, Option<&Point>, Option<&Line>), With<Selected>>();
if let Some(first) = selected.iter(&self.world).next() {
} else {
ui.label("Select an entity to add a relation");
Tool::AddRelation => {
if let Some(first) = selected.iter().next() {
} else {
ui.label("Select an entity to add a relation");
}
}
}
_ => {
ui.label(":)");
}
_ => {
ui.label(":)");
}
});
});
}
fn bottom_panel(ctx: Res<ContextRes>) {
egui::TopBottomPanel::bottom("bottom_panel").show(&ctx, |ui| {
ui.horizontal(|ui| {
ui.label("Status:");
});
});
}
struct MyApp {
world: World,
update_schedule: Schedule,
}
impl Default for MyApp {
fn default() -> Self {
Self::new()
}
}
impl MyApp {
fn new() -> Self {
let mut world = World::default();
world.init_resource::<Tool>();
let mut init_sched = Schedule::new();
init_sched.add_system(init);
init_sched.run(&mut world);
let mut update = Schedule::default();
update.add_systems((prepare, toolbar, side_panel, bottom_panel, central_panel).chain());
Self {
world,
update_schedule: update,
}
}
}
impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
ctx.request_repaint();
ctx.set_visuals(egui::Visuals::dark());
self.world.insert_resource(ContextRes(ctx.clone()));
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| self.show_toolbar(ui));
self.update_schedule.run(&mut self.world);
egui::CentralPanel::default()
.frame(egui::Frame::none())
.show(ctx, |ui| {
self.show_entities(ui);
});
egui::SidePanel::right("right_panel")
.resizable(true)
.default_width(150.0)
.width_range(80.0..=200.0)
.show(ctx, |ui| self.show_side_panel(ui));
egui::TopBottomPanel::bottom("bottom_panel").show(ctx, |ui| {
ui.horizontal(|ui| {
ui.label("Status: ");
});
});
self.world.remove_resource::<ContextRes>();
}
}