extract ui to module
This commit is contained in:
parent
4eccbf78e0
commit
6856a5830f
227
src/main.rs
227
src/main.rs
@ -1,224 +1,18 @@
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
query::WorldQuery,
|
||||
system::{Commands, IntoSystem, Query, ReadOnlySystem, Res, ResMut, System},
|
||||
};
|
||||
use eframe::{
|
||||
egui::{self, CursorIcon, Painter, Response, Sense},
|
||||
emath::{Pos2, Rect, RectTransform, Vec2},
|
||||
epaint::{Color32, Hsva, Stroke},
|
||||
};
|
||||
use geometry::{Line, LineBundle, Point, PointId, PointPos, PointPosQueryMut};
|
||||
use bevy_ecs::prelude::*;
|
||||
use eframe::egui;
|
||||
|
||||
mod geometry;
|
||||
pub mod optimization;
|
||||
mod relations;
|
||||
#[cfg(feature = "trace")]
|
||||
mod tracing;
|
||||
mod ui;
|
||||
|
||||
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.));
|
||||
geometry::insert_point_at(&mut commands, (0., -10.));
|
||||
commands.spawn(LineBundle::new(p1, p2));
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct ContextRes(egui::Context);
|
||||
|
||||
impl Deref for ContextRes {
|
||||
type Target = egui::Context;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(ctx: Res<ContextRes>) {
|
||||
ctx.request_repaint();
|
||||
ctx.set_visuals(egui::Visuals::dark());
|
||||
}
|
||||
|
||||
fn toolbar(
|
||||
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);
|
||||
|
||||
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[system_set(base)]
|
||||
pub enum ShowEntitiesStage {
|
||||
Update,
|
||||
Input,
|
||||
Tools,
|
||||
PostTools,
|
||||
Paint,
|
||||
}
|
||||
|
||||
impl Default for ShowEntitiesSchedule {
|
||||
fn default() -> Self {
|
||||
let mut schedule = Schedule::new();
|
||||
schedule.configure_sets(
|
||||
(
|
||||
ShowEntitiesStage::Update,
|
||||
ShowEntitiesStage::Input,
|
||||
ShowEntitiesStage::Tools,
|
||||
ShowEntitiesStage::PostTools,
|
||||
ShowEntitiesStage::Paint,
|
||||
)
|
||||
.chain(),
|
||||
);
|
||||
schedule.add_systems(
|
||||
(geometry::update_point_pos, geometry::update_line_pos)
|
||||
.in_base_set(ShowEntitiesStage::Update),
|
||||
);
|
||||
schedule.add_systems(
|
||||
(update_hover_point, update_hover_line).in_base_set(ShowEntitiesStage::Input),
|
||||
);
|
||||
schedule.add_systems(
|
||||
(
|
||||
select_tool.run_if(is_tool_active(Tool::Select)),
|
||||
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(ShowEntitiesStage::Tools),
|
||||
);
|
||||
schedule
|
||||
.add_system(geometry::remove_dangling_lines.in_base_set(ShowEntitiesStage::PostTools));
|
||||
schedule.add_systems(
|
||||
(paint_lines, paint_points)
|
||||
.chain()
|
||||
.in_base_set(ShowEntitiesStage::Paint),
|
||||
);
|
||||
Self(schedule)
|
||||
}
|
||||
}
|
||||
|
||||
fn central_panel(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(),
|
||||
};
|
||||
|
||||
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));
|
||||
|
||||
schedule.0.run(world);
|
||||
|
||||
world.remove_resource::<PainterRes>();
|
||||
world.remove_resource::<ResponseRes>();
|
||||
world.remove_resource::<ToScreen>();
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(WorldQuery)]
|
||||
struct SelectableEntity<'a> {
|
||||
id: Entity,
|
||||
point: Option<&'a Point>,
|
||||
line: Option<&'a Line>,
|
||||
}
|
||||
|
||||
fn side_panel_ui(
|
||||
ui: &mut egui::Ui,
|
||||
selected: Query<SelectableEntity, With<Selected>>,
|
||||
tool: Res<Tool>,
|
||||
) {
|
||||
let tool = *tool;
|
||||
ui.vertical(|ui| match tool {
|
||||
Tool::Select => {
|
||||
let mut count = 0;
|
||||
selected.for_each(|sel| {
|
||||
count += 1;
|
||||
if sel.point.is_some() {
|
||||
ui.label(format!("Selected point {}", sel.id.index()));
|
||||
} else if sel.line.is_some() {
|
||||
ui.label(format!("Selected line {}", sel.id.index()));
|
||||
}
|
||||
});
|
||||
if count == 0 {
|
||||
ui.label("Nothing selected");
|
||||
}
|
||||
}
|
||||
Tool::Move => {
|
||||
let mut count = 0;
|
||||
selected.for_each(|sel| {
|
||||
count += 1;
|
||||
if sel.point.is_some() {
|
||||
ui.label(format!("Selected point {}", sel.id.index()));
|
||||
} else if sel.line.is_some() {
|
||||
ui.label(format!("Selected line {}", sel.id.index()));
|
||||
}
|
||||
});
|
||||
if count == 0 {
|
||||
ui.label("Nothing selected");
|
||||
}
|
||||
}
|
||||
Tool::AddPoint => {
|
||||
ui.label("Click to add a point");
|
||||
}
|
||||
Tool::AddLine => {
|
||||
ui.label("Click to add a line");
|
||||
}
|
||||
Tool::AddRelation => {
|
||||
ui.label("Click to add a relation");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn side_panel(
|
||||
ctx: Res<ContextRes>,
|
||||
selected: Query<SelectableEntity, 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| side_panel_ui(ui, selected, tool));
|
||||
}
|
||||
|
||||
fn bottom_panel(ctx: Res<ContextRes>) {
|
||||
egui::TopBottomPanel::bottom("bottom_panel").show(&ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Status:");
|
||||
});
|
||||
});
|
||||
commands.spawn(geometry::LineBundle::new(p1, p2));
|
||||
}
|
||||
|
||||
struct MyApp {
|
||||
@ -231,6 +25,7 @@ impl Default for MyApp {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl MyApp {
|
||||
fn new() -> Self {
|
||||
let mut world = World::default();
|
||||
@ -238,27 +33,29 @@ impl MyApp {
|
||||
#[cfg(feature = "trace")]
|
||||
tracing::setup(&mut world);
|
||||
|
||||
world.init_resource::<ui::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());
|
||||
let mut update_schedule = Schedule::default();
|
||||
ui::add_to_schedule(&mut update_schedule);
|
||||
|
||||
Self {
|
||||
world,
|
||||
update_schedule: update,
|
||||
update_schedule,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for MyApp {
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
self.world.insert_resource(ContextRes(ctx.clone()));
|
||||
self.world.insert_resource(ui::ContextRes(ctx.clone()));
|
||||
|
||||
self.update_schedule.run(&mut self.world);
|
||||
|
||||
self.world.remove_resource::<ContextRes>();
|
||||
self.world.remove_resource::<ui::ContextRes>();
|
||||
}
|
||||
}
|
||||
|
||||
|
570
src/ui.rs
Normal file
570
src/ui.rs
Normal file
@ -0,0 +1,570 @@
|
||||
use std::ops::Deref;
|
||||
|
||||
use bevy_ecs::{prelude::*, query::WorldQuery, system::ReadOnlySystem};
|
||||
use eframe::{
|
||||
egui::{self, CursorIcon, Painter, Response, Sense},
|
||||
emath::RectTransform,
|
||||
epaint::{Color32, Hsva, Pos2, Rect, Stroke, Vec2},
|
||||
};
|
||||
|
||||
use crate::geometry::{self};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Resource)]
|
||||
pub enum Tool {
|
||||
Select,
|
||||
Move,
|
||||
AddPoint,
|
||||
AddLine,
|
||||
AddRelation,
|
||||
}
|
||||
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for Tool {
|
||||
fn default() -> Self {
|
||||
Tool::Select
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
struct UiRes {
|
||||
response: Response,
|
||||
painter: Painter,
|
||||
to_screen: ToScreen,
|
||||
}
|
||||
struct ToScreen(RectTransform);
|
||||
|
||||
impl ToScreen {
|
||||
fn transform_pos(&self, pos: &geometry::PointPos) -> Pos2 {
|
||||
self.0 * Pos2::new(pos.x as f32, pos.y as f32)
|
||||
}
|
||||
|
||||
fn inverse_transform(&self, pos: Pos2) -> Pos2 {
|
||||
self.0.inverse() * pos
|
||||
}
|
||||
|
||||
fn inverse_transform_to_point(&self, pos: Pos2) -> geometry::PointPos {
|
||||
let pos = self.inverse_transform(pos);
|
||||
geometry::PointPos::new(pos.x as f64, pos.y as f64)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component)]
|
||||
struct Hovered;
|
||||
|
||||
#[derive(Component)]
|
||||
struct Selected;
|
||||
|
||||
fn color_for_var_status(status: geometry::VarStatus) -> Hsva {
|
||||
use geometry::VarStatus::*;
|
||||
match status {
|
||||
Free => Hsva::new(200. / 360., 0.90, 0.80, 1.0),
|
||||
Dependent => todo!(),
|
||||
Unique => todo!(),
|
||||
Overconstrained => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
const POINT_RADIUS: f32 = 3.0;
|
||||
|
||||
fn update_hover_point(
|
||||
ui: Res<UiRes>,
|
||||
points: Query<(geometry::PointId, &geometry::ComputedPointPos)>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
points.for_each(|(id, pos)| {
|
||||
let hovered = if let Some(hover_pos) = ui.response.hover_pos() {
|
||||
let center = ui.to_screen.transform_pos(pos);
|
||||
|
||||
(hover_pos - center).length() < (POINT_RADIUS * 3.)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if hovered {
|
||||
commands.entity(id).insert(Hovered);
|
||||
} else {
|
||||
commands.entity(id).remove::<Hovered>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn update_hover_line(
|
||||
ui: Res<UiRes>,
|
||||
lines: Query<(Entity, &geometry::ComputedLinePos)>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
lines.for_each(|(id, pos)| {
|
||||
let hovered = if let Some(hover_pos) = ui.response.hover_pos() {
|
||||
let points = [
|
||||
ui.to_screen.transform_pos(&pos.start),
|
||||
ui.to_screen.transform_pos(&pos.end),
|
||||
];
|
||||
|
||||
let b = points[1] - points[0];
|
||||
let a = hover_pos - points[0];
|
||||
let p = a.dot(b) / b.dot(b);
|
||||
let perp = a - (p * b);
|
||||
((0.)..=1.).contains(&p) && perp.length() < 5.0
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if hovered {
|
||||
commands.entity(id).insert(Hovered);
|
||||
} else {
|
||||
commands.entity(id).remove::<Hovered>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn select_tool(
|
||||
ui: Res<UiRes>,
|
||||
hovered: Query<Entity, With<Hovered>>,
|
||||
selected: Query<Entity, With<Selected>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
ui.response.ctx.output_mut(|output| {
|
||||
output.cursor_icon = if !hovered.is_empty() {
|
||||
CursorIcon::PointingHand
|
||||
} else {
|
||||
CursorIcon::Default
|
||||
}
|
||||
});
|
||||
|
||||
if ui.response.clicked() {
|
||||
if !ui.response.ctx.input(|input| input.modifiers.shift) {
|
||||
selected.for_each(|selected| {
|
||||
commands.entity(selected).remove::<Selected>();
|
||||
});
|
||||
}
|
||||
// TODO: choose which to select
|
||||
if let Some(hovered) = hovered.iter().next() {
|
||||
commands.entity(hovered).insert(Selected);
|
||||
}
|
||||
}
|
||||
|
||||
if ui
|
||||
.response
|
||||
.ctx
|
||||
.input(|input| input.key_pressed(egui::Key::Escape))
|
||||
{
|
||||
selected.for_each(|selected| {
|
||||
commands.entity(selected).remove::<Selected>();
|
||||
});
|
||||
} else if ui
|
||||
.response
|
||||
.ctx
|
||||
.input(|input| input.key_pressed(egui::Key::Delete))
|
||||
{
|
||||
selected.for_each(|selected| {
|
||||
commands.entity(selected).despawn();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct DragDelta(Vec2);
|
||||
|
||||
// TODO: move other entities
|
||||
fn move_tool(
|
||||
ui: Res<UiRes>,
|
||||
mut drag_delta: Local<DragDelta>,
|
||||
mut point_pos: geometry::PointPosQueryMut,
|
||||
hovered: Query<(Entity, &geometry::Point), With<Hovered>>,
|
||||
selected: Query<(Entity, &geometry::Point), With<Selected>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let response = &ui.response;
|
||||
let to_screen = &ui.to_screen;
|
||||
let hover_pos = response.hover_pos().unwrap();
|
||||
|
||||
response.ctx.output_mut(|o| {
|
||||
o.cursor_icon = if !selected.is_empty() {
|
||||
CursorIcon::Grabbing
|
||||
} else if !hovered.is_empty() {
|
||||
CursorIcon::Grab
|
||||
} else {
|
||||
CursorIcon::Move
|
||||
}
|
||||
});
|
||||
|
||||
let selected = if response.drag_started() {
|
||||
// TODO: choose which to select
|
||||
let to_select = hovered.iter().next();
|
||||
if let Some(hovered) = to_select {
|
||||
commands.entity(hovered.0).insert(Selected);
|
||||
}
|
||||
to_select
|
||||
} else if response.drag_released() {
|
||||
selected.for_each(|selected| {
|
||||
commands.entity(selected.0).remove::<Selected>();
|
||||
});
|
||||
drag_delta.0 = Vec2::ZERO;
|
||||
None
|
||||
} else {
|
||||
selected.iter().next()
|
||||
};
|
||||
|
||||
if let Some((_, point)) = selected {
|
||||
if response.drag_started() {
|
||||
let drag_point_pos = point_pos.get(point).unwrap();
|
||||
drag_delta.0 = hover_pos - to_screen.transform_pos(&drag_point_pos);
|
||||
}
|
||||
let move_to = to_screen.inverse_transform_to_point(hover_pos - drag_delta.0);
|
||||
point_pos.set(point, move_to).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn add_point(commands: &mut Commands, pos: Pos2, to_screen: &ToScreen) -> geometry::PointId {
|
||||
let point_pos = to_screen.inverse_transform(pos);
|
||||
let point_pos = (point_pos.x as f64, point_pos.y as f64);
|
||||
geometry::insert_point_at(commands, point_pos)
|
||||
}
|
||||
|
||||
fn add_point_tool(ui: Res<UiRes>, mut commands: Commands) {
|
||||
let hover_pos = ui.response.hover_pos().unwrap();
|
||||
if ui.response.clicked() {
|
||||
add_point(&mut commands, hover_pos, &ui.to_screen);
|
||||
} else {
|
||||
ui.painter
|
||||
.circle_filled(hover_pos, POINT_RADIUS, Color32::WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_line_tool(
|
||||
ui: Res<UiRes>,
|
||||
hovered: Query<Entity, (With<Hovered>, With<geometry::Point>)>,
|
||||
selected: Query<(Entity, &geometry::ComputedPointPos), With<Selected>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let UiRes {
|
||||
response,
|
||||
painter,
|
||||
to_screen,
|
||||
} = &*ui;
|
||||
let hover_pos = response.hover_pos().unwrap();
|
||||
|
||||
match (selected.iter().next(), response.clicked()) {
|
||||
(None, false) => {
|
||||
painter.circle_filled(hover_pos, POINT_RADIUS, Color32::DARK_GRAY);
|
||||
}
|
||||
(None, true) => {
|
||||
let point_id = match hovered.iter().next() {
|
||||
Some(hovered) => hovered,
|
||||
None => add_point(&mut commands, hover_pos, &to_screen),
|
||||
};
|
||||
commands.entity(point_id).insert(Selected);
|
||||
}
|
||||
(Some((_, start_point_pos)), false) => {
|
||||
let points = [to_screen.transform_pos(start_point_pos), hover_pos];
|
||||
|
||||
let stroke = Stroke::new(2.0, Color32::DARK_GRAY);
|
||||
|
||||
painter.line_segment(points, stroke);
|
||||
|
||||
painter.circle_filled(hover_pos, POINT_RADIUS, Color32::DARK_GRAY);
|
||||
}
|
||||
(Some((start_point_id, _)), true) => {
|
||||
// TODO: add point if no hover point
|
||||
let end_point = hovered
|
||||
.iter()
|
||||
.next()
|
||||
.unwrap_or_else(|| add_point(&mut commands, hover_pos, &to_screen));
|
||||
|
||||
let line = geometry::LineBundle::new(start_point_id, end_point);
|
||||
commands.spawn(line);
|
||||
|
||||
selected.for_each(|(selected_id, _)| {
|
||||
commands.entity(selected_id).remove::<Selected>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn add_relation_tool(
|
||||
ui: Res<UiRes>,
|
||||
hovered: Query<Entity, With<Hovered>>,
|
||||
selected: Query<Entity, With<Selected>>,
|
||||
mut commands: Commands,
|
||||
) {
|
||||
let response = &ui.response;
|
||||
response.ctx.output_mut(|o| {
|
||||
o.cursor_icon = if !hovered.is_empty() {
|
||||
CursorIcon::PointingHand
|
||||
} else {
|
||||
CursorIcon::Default
|
||||
}
|
||||
});
|
||||
|
||||
if response.clicked() {
|
||||
// TODO: choose which to select
|
||||
if let Some(hovered) = hovered.iter().next() {
|
||||
commands.entity(hovered).insert(Selected);
|
||||
} else {
|
||||
selected.for_each(|selected| {
|
||||
commands.entity(selected).remove::<Selected>();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_hovered(ui: Res<UiRes>) -> bool {
|
||||
ui.response.hover_pos().is_some()
|
||||
}
|
||||
|
||||
fn is_tool_active(tool: Tool) -> impl System<In = (), Out = bool> + ReadOnlySystem {
|
||||
IntoSystem::into_system(move |active_tool: Res<Tool>| *active_tool == tool)
|
||||
}
|
||||
|
||||
fn paint_lines(
|
||||
ui: Res<UiRes>,
|
||||
lines: Query<(Entity, &geometry::ComputedLinePos)>,
|
||||
hovered: Query<(), With<Hovered>>,
|
||||
selected: Query<(), With<Selected>>,
|
||||
) {
|
||||
lines.for_each(|(id, pos)| {
|
||||
let points = [
|
||||
ui.to_screen.transform_pos(&pos.start),
|
||||
ui.to_screen.transform_pos(&pos.end),
|
||||
];
|
||||
|
||||
let mut color = color_for_var_status(pos.status);
|
||||
color.v -= 0.6;
|
||||
if hovered.contains(id) || selected.contains(id) {
|
||||
color.s -= 0.8;
|
||||
}
|
||||
|
||||
let stroke = Stroke::new(2.0, color);
|
||||
|
||||
ui.painter.line_segment(points, stroke);
|
||||
});
|
||||
}
|
||||
|
||||
fn paint_points(
|
||||
ui: Res<UiRes>,
|
||||
points: Query<(Entity, &geometry::ComputedPointPos)>,
|
||||
hovered: Query<(), With<Hovered>>,
|
||||
selected: Query<(), With<Selected>>,
|
||||
) {
|
||||
points.for_each(|(id, pos)| {
|
||||
let center = ui.to_screen.transform_pos(pos);
|
||||
|
||||
let color = color_for_var_status(pos.status);
|
||||
let stroke = if selected.contains(id) || hovered.contains(id) {
|
||||
// color.s -= 0.8;
|
||||
Stroke::new(1.0, Color32::WHITE)
|
||||
} else {
|
||||
Stroke::default()
|
||||
};
|
||||
ui.painter.circle(center, POINT_RADIUS, color, stroke);
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(Resource)]
|
||||
pub struct ContextRes(pub egui::Context);
|
||||
|
||||
impl Deref for ContextRes {
|
||||
type Target = egui::Context;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
fn prepare(ctx: Res<ContextRes>) {
|
||||
// ctx.request_repaint();
|
||||
ctx.set_visuals(egui::Visuals::dark());
|
||||
}
|
||||
|
||||
fn toolbar(
|
||||
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);
|
||||
|
||||
#[derive(SystemSet, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[system_set(base)]
|
||||
pub enum ShowEntitiesStage {
|
||||
Update,
|
||||
UpdateFlush,
|
||||
Input,
|
||||
InputFlush,
|
||||
Tools,
|
||||
PostTools,
|
||||
ToolsFlush,
|
||||
Paint,
|
||||
}
|
||||
|
||||
impl Default for ShowEntitiesSchedule {
|
||||
fn default() -> Self {
|
||||
let mut schedule = Schedule::new();
|
||||
schedule
|
||||
.configure_sets(
|
||||
(
|
||||
ShowEntitiesStage::Update,
|
||||
ShowEntitiesStage::UpdateFlush,
|
||||
ShowEntitiesStage::Input,
|
||||
ShowEntitiesStage::InputFlush,
|
||||
ShowEntitiesStage::Tools,
|
||||
ShowEntitiesStage::ToolsFlush,
|
||||
ShowEntitiesStage::PostTools,
|
||||
ShowEntitiesStage::Paint,
|
||||
)
|
||||
.chain(),
|
||||
)
|
||||
.add_systems(
|
||||
(geometry::update_point_pos, geometry::update_line_pos)
|
||||
.in_base_set(ShowEntitiesStage::Update),
|
||||
)
|
||||
.add_system(apply_system_buffers.in_base_set(ShowEntitiesStage::UpdateFlush))
|
||||
.add_systems(
|
||||
(update_hover_point, update_hover_line).in_base_set(ShowEntitiesStage::Input),
|
||||
)
|
||||
.add_system(apply_system_buffers.in_base_set(ShowEntitiesStage::InputFlush))
|
||||
.add_systems(
|
||||
(
|
||||
select_tool.run_if(is_tool_active(Tool::Select)),
|
||||
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(ShowEntitiesStage::Tools),
|
||||
)
|
||||
.add_system(geometry::remove_dangling_lines.in_base_set(ShowEntitiesStage::PostTools))
|
||||
.add_system(apply_system_buffers.in_base_set(ShowEntitiesStage::ToolsFlush))
|
||||
.add_systems(
|
||||
(paint_lines, paint_points)
|
||||
.chain()
|
||||
.in_base_set(ShowEntitiesStage::Paint),
|
||||
);
|
||||
Self(schedule)
|
||||
}
|
||||
}
|
||||
|
||||
fn central_panel(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(),
|
||||
};
|
||||
|
||||
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,
|
||||
));
|
||||
world.insert_resource(UiRes {
|
||||
response,
|
||||
painter,
|
||||
to_screen,
|
||||
});
|
||||
|
||||
schedule.0.run(world);
|
||||
|
||||
world.remove_resource::<UiRes>();
|
||||
});
|
||||
}
|
||||
|
||||
#[derive(WorldQuery)]
|
||||
struct SelectableEntity<'a> {
|
||||
id: Entity,
|
||||
point: Option<&'a geometry::Point>,
|
||||
line: Option<&'a geometry::Line>,
|
||||
}
|
||||
|
||||
fn side_panel_ui(
|
||||
ui: &mut egui::Ui,
|
||||
selected: Query<SelectableEntity, With<Selected>>,
|
||||
tool: Res<Tool>,
|
||||
) {
|
||||
let tool = *tool;
|
||||
ui.vertical(|ui| match tool {
|
||||
Tool::Select => {
|
||||
let mut count = 0;
|
||||
selected.for_each(|sel| {
|
||||
count += 1;
|
||||
if sel.point.is_some() {
|
||||
ui.label(format!("Selected point {}", sel.id.index()));
|
||||
} else if sel.line.is_some() {
|
||||
ui.label(format!("Selected line {}", sel.id.index()));
|
||||
}
|
||||
});
|
||||
if count == 0 {
|
||||
ui.label("Nothing selected");
|
||||
}
|
||||
}
|
||||
Tool::Move => {
|
||||
let mut count = 0;
|
||||
selected.for_each(|sel| {
|
||||
count += 1;
|
||||
if sel.point.is_some() {
|
||||
ui.label(format!("Selected point {}", sel.id.index()));
|
||||
} else if sel.line.is_some() {
|
||||
ui.label(format!("Selected line {}", sel.id.index()));
|
||||
}
|
||||
});
|
||||
if count == 0 {
|
||||
ui.label("Nothing selected");
|
||||
}
|
||||
}
|
||||
Tool::AddPoint => {
|
||||
ui.label("Click to add a point");
|
||||
}
|
||||
Tool::AddLine => {
|
||||
ui.label("Click to add a line");
|
||||
}
|
||||
Tool::AddRelation => {
|
||||
ui.label("Click to add a relation");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn side_panel(
|
||||
ctx: Res<ContextRes>,
|
||||
selected: Query<SelectableEntity, 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| side_panel_ui(ui, selected, tool));
|
||||
}
|
||||
|
||||
fn bottom_panel(ctx: Res<ContextRes>) {
|
||||
egui::TopBottomPanel::bottom("bottom_panel").show(&ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Status:");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn add_to_schedule(schedule: &mut Schedule) {
|
||||
schedule.add_systems((prepare, toolbar, side_panel, bottom_panel, central_panel).chain());
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user