Skip to content

Commit

Permalink
Issue 63 - Add components window (#69)
Browse files Browse the repository at this point in the history
* Add component window spawns on right click

* limits egui context

* fmt

* Fix camera input blocking

* Fix input blocking from interact

* cargo fmt

---------

Co-authored-by: a.yamaev <[email protected]>
  • Loading branch information
naomijub and ayamaev-se authored Nov 23, 2023
1 parent 65266fd commit ded7991
Show file tree
Hide file tree
Showing 3 changed files with 168 additions and 89 deletions.
192 changes: 106 additions & 86 deletions src/editor/ui/inspector/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub struct SpaceInspectorPlugin;
impl Plugin for SpaceInspectorPlugin {
fn build(&self, app: &mut App) {
app.init_resource::<InspectState>();
app.init_resource::<FilterComponentState>();

app.editor_tab_by_trait(
crate::prelude::EditorTabName::Inspector,
Expand Down Expand Up @@ -82,19 +83,15 @@ pub fn mut_untyped_split(mut mut_untyped: MutUntyped<'_>) -> (PtrMut<'_>, impl F
}

/// Just state of inspector panel
#[derive(Resource)]
#[derive(Resource, Default)]
struct InspectState {
component_add_filter: String,
commands: Vec<InspectCommand>,
show_add_component_window: bool,
}

impl Default for InspectState {
fn default() -> Self {
Self {
component_add_filter: "".to_string(),
commands: vec![],
}
}
#[derive(Resource, Default)]
struct FilterComponentState {
component_add_filter: String,
}

enum InspectCommand {
Expand Down Expand Up @@ -123,17 +120,20 @@ fn execute_inspect_command(

/// System to show inspector panel
pub fn inspect(ui: &mut egui::Ui, world: &mut World) {
let selected = world
let selected_entity = world
.query_filtered::<Entity, With<Selected>>()
.iter(world)
.collect::<Vec<_>>();
.get_single(world);

let Ok(selected_entity) = selected_entity else {
return;
};

let editor_registry = world.resource::<EditorRegistry>().clone();
let all_registry = editor_registry.registry.clone();
let registry = all_registry.read();
let app_registry = world.resource::<AppTypeRegistry>().clone();
let world_registry = app_registry.read();
let disable_pan_orbit = false;
let mut disable_pan_orbit = false;

//Collet data about all components
let mut components_id = Vec::new();
Expand All @@ -160,83 +160,102 @@ pub fn inspect(ui: &mut egui::Ui, world: &mut World) {
};
let mut env = InspectorUi::for_bevy(&world_registry, &mut cx);

egui::ScrollArea::vertical().show(ui, |ui| {
for e in selected.iter() {
if let Some(e) = cell.get_entity(*e) {
let mut name;
if let Some(name_struct) = unsafe { e.get::<Name>() } {
name = name_struct.as_str().to_string();
if name.is_empty() {
name = format!("{:?} (empty name)", e.id());
}
} else {
name = format!("{:?}", e.id());
//Open context window by right mouse button click
//ui.interact blocks all control of inspector window
if ui
.interact(ui.min_rect(), "painter".into(), egui::Sense::click())
.secondary_clicked()
{
state.show_add_component_window = true;
}

let components_area = egui::ScrollArea::vertical().show(ui, |ui| {
if let Some(e) = cell.get_entity(selected_entity) {
let mut name;
if let Some(name_struct) = unsafe { e.get::<Name>() } {
name = name_struct.as_str().to_string();
if name.is_empty() {
name = format!("{:?} (empty name)", e.id());
}
ui.heading(&name);
ui.label("Components:");
let e_id = e.id().index();
egui::Grid::new(format!("{e_id}")).show(ui, |ui| {
for (c_id, t_id, name) in &components_id {
if let Some(data) = unsafe { e.get_mut_by_id(*c_id) } {
let registration = registry.get(*t_id).unwrap();
if let Some(reflect_from_ptr) = registration.data::<ReflectFromPtr>() {
let (ptr, mut set_changed) = mut_untyped_split(data);

let value = unsafe { reflect_from_ptr.from_ptr_mut()(ptr) };

if !editor_registry.silent.contains(&registration.type_id()) {
ui.push_id(format!("{:?}-{}", &e.id(), &name), |ui| {
ui.collapsing(name, |ui| {
ui.push_id(
format!("content-{:?}-{}", &e.id(), &name),
|ui| {
if env.ui_for_reflect_with_options(
value,
ui,
ui.id(),
&(),
) {
set_changed();
}
},
);
});
} else {
name = format!("{:?}", e.id());
}
ui.heading(&name);
ui.label("Components:");
let e_id = e.id().index();
egui::Grid::new(format!("{e_id}")).show(ui, |ui| {
for (c_id, t_id, name) in &components_id {
if let Some(data) = unsafe { e.get_mut_by_id(*c_id) } {
let registration = registry.get(*t_id).unwrap();
if let Some(reflect_from_ptr) = registration.data::<ReflectFromPtr>() {
let (ptr, mut set_changed) = mut_untyped_split(data);

let value = unsafe { reflect_from_ptr.from_ptr_mut()(ptr) };

if !editor_registry.silent.contains(&registration.type_id()) {
ui.push_id(format!("{:?}-{}", &e.id(), &name), |ui| {
ui.collapsing(name, |ui| {
ui.push_id(
format!("content-{:?}-{}", &e.id(), &name),
|ui| {
if env.ui_for_reflect_with_options(
value,
ui,
ui.id(),
&(),
) {
set_changed();
}
},
);
});

ui.push_id(
format!("del component {:?}-{}", &e.id(), &name),
|ui| {
//must be on top
ui.with_layout(
egui::Layout::top_down(egui::Align::Min),
|ui| {
if ui.button("X").clicked() {
commands.push(
InspectCommand::RemoveComponent(
e.id(),
*t_id,
),
);
}
},
);
},
);
ui.end_row();
}
});

ui.push_id(
format!("del component {:?}-{}", &e.id(), &name),
|ui| {
//must be on top
ui.with_layout(
egui::Layout::top_down(egui::Align::Min),
|ui| {
if ui.button("X").clicked() {
commands.push(InspectCommand::RemoveComponent(
e.id(),
*t_id,
));
}
},
);
},
);
ui.end_row();
}
}
}
});
}
});

ui.separator();
}
ui.separator();
}
});

ui.label("Add component");
ui.text_edit_singleline(&mut state.component_add_filter);
let lower_filter = state.component_add_filter.to_lowercase();
egui::ScrollArea::vertical().show(ui, |ui| {
//Open context window by button
if ui.button("Add component").clicked() {
state.show_add_component_window = true;
}

let add_responce = egui::Window::new("Add component")
.open(&mut state.show_add_component_window)
.resizable(true)
.scroll2([false, true])
.default_width(120.)
.default_height(300.)
.default_pos(components_area.inner_rect.center_bottom())
.show(ui.ctx(), |ui: &mut egui::Ui| {
// disable_pan_orbit = true;
let mut state = unsafe { cell.get_resource_mut::<FilterComponentState>().unwrap() };
ui.text_edit_singleline(&mut state.component_add_filter);
let lower_filter = state.component_add_filter.to_lowercase();
egui::Grid::new("Component grid").show(ui, |ui| {
let _counter = 0;
for (c_id, _t_id, name) in &components_id {
Expand All @@ -249,16 +268,17 @@ pub fn inspect(ui: &mut egui::Ui, world: &mut World) {
.unwrap()
.type_id()
.unwrap();
for e in selected.iter() {
commands.push(InspectCommand::AddComponent(*e, id));
}
commands.push(InspectCommand::AddComponent(selected_entity, id));
}
ui.end_row();
}
}
});
});
});

if ui.ui_contains_pointer() || ui.ctx().is_pointer_over_area() || ui.ctx().is_using_pointer() {
disable_pan_orbit = true;
}

state.commands = commands;

Expand Down
11 changes: 8 additions & 3 deletions src/editor/ui/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//This module contains ui logics, which will be work through events with editor core module and prefab module
mod mouse_check;

pub mod asset_inspector;
pub use asset_inspector::*;
Expand Down Expand Up @@ -35,7 +36,10 @@ use bevy_egui::{egui, EguiContext};

use crate::{EditorSet, EditorState};

use self::tools::gizmo::GizmoTool;
use self::{
mouse_check::{pointer_context_check, MouseCheck},
tools::gizmo::GizmoTool,
};

use super::{
core::{SelectedPlugin, ToolExt, UndoRedo},
Expand Down Expand Up @@ -63,7 +67,7 @@ impl Plugin for EditorUiPlugin {
app.add_plugins(SelectedPlugin);
}

app.add_plugins(bot_menu::BotMenuPlugin);
app.add_plugins((bot_menu::BotMenuPlugin, MouseCheck));

app.configure_sets(
Update,
Expand All @@ -79,8 +83,9 @@ impl Plugin for EditorUiPlugin {
(
show_editor_ui
.before(update_pan_orbit)
.before(super::ui_camera_block)
.after(bot_menu::bot_menu),
set_camera_viewport,
set_camera_viewport.run_if(pointer_context_check()),
)
.in_set(UiSystemSet),
);
Expand Down
54 changes: 54 additions & 0 deletions src/editor/ui/mouse_check.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use bevy::{prelude::*, window::PrimaryWindow};
use bevy_egui::EguiContexts;

#[derive(Default)]
pub struct MouseCheck;

impl Plugin for MouseCheck {
fn build(&self, app: &mut App) {
app.init_resource::<PointerContextCheck>()
.add_systems(Startup, initialize_mouse_context)
.add_systems(PreUpdate, update_mouse_context);
}
}

#[derive(Resource)]
pub struct PointerContextCheck {
pointer_is_valid: bool,
primary_window: Option<Entity>,
}

impl Default for PointerContextCheck {
fn default() -> Self {
Self {
pointer_is_valid: true,
primary_window: None,
}
}
}

pub fn initialize_mouse_context(
mut pointer_ctx: ResMut<PointerContextCheck>,
window_q: Query<Entity, With<PrimaryWindow>>,
) {
if let Ok(window_id) = window_q.get_single() {
pointer_ctx.primary_window = Some(window_id);
} else {
error!("could not get Primary Window");
}
}

pub fn update_mouse_context(
mut pointer_ctx: ResMut<PointerContextCheck>,
mut egui_ctxs: EguiContexts,
) {
if let Some(window_id) = pointer_ctx.primary_window {
pointer_ctx.pointer_is_valid = !egui_ctxs
.ctx_for_window_mut(window_id)
.wants_pointer_input();
}
}

pub fn pointer_context_check() -> impl Fn(Res<PointerContextCheck>) -> bool + Clone {
move |egui_mouse_check: Res<PointerContextCheck>| egui_mouse_check.pointer_is_valid
}

0 comments on commit ded7991

Please sign in to comment.