mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-16 00:03:29 +00:00
Fix hierarchy selection issues
This commit is contained in:
@@ -6,8 +6,8 @@ use bevy::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
input::{Action, CameraMotion, InputAction, Lifecycle, Selection},
|
||||
interaction::Selected,
|
||||
input::{self, Action, CameraMotion, InputAction, Lifecycle},
|
||||
interaction::Selection,
|
||||
math::{
|
||||
Easing, InterpPosition, InterpVelocity, Interpolation, InterpolationSystem, NearlyEq,
|
||||
NearlyZero, Velocity,
|
||||
@@ -155,20 +155,25 @@ fn process_physical_inputs(
|
||||
|
||||
fn process_focus_inputs(
|
||||
mut cmd: Commands,
|
||||
r_selection: Option<Res<Selection>>,
|
||||
mut input: EventReader<InputAction>,
|
||||
mut q_camera: Query<(Entity, &CameraController, &mut Velocity)>,
|
||||
q_interp_vel: Query<&InterpVelocity>,
|
||||
mut q_interp_pos: Query<&mut InterpPosition>,
|
||||
q_selected_xform: Query<&GlobalTransform, With<Selected>>,
|
||||
q_global_xforms: Query<&GlobalTransform>,
|
||||
) {
|
||||
let Some(selection) = r_selection else {
|
||||
return;
|
||||
};
|
||||
|
||||
if input.read().any(|action| {
|
||||
matches!(
|
||||
action,
|
||||
InputAction(Lifecycle::Stop, Action::Selection(Selection::Focus))
|
||||
InputAction(Lifecycle::Stop, Action::Selection(input::Selection::Focus))
|
||||
)
|
||||
}) {
|
||||
let Ok(target) = q_selected_xform
|
||||
.get_single()
|
||||
let Ok(target) = q_global_xforms
|
||||
.get(**selection)
|
||||
.map(|xform| xform.translation())
|
||||
else {
|
||||
return;
|
||||
|
||||
@@ -49,43 +49,52 @@ impl Plugin for SelectionPlugin {
|
||||
.run_if(resource_changed_or_removed::<Selection>()),
|
||||
);
|
||||
|
||||
app.add_systems(Update, sync_editor_selected);
|
||||
app.add_systems(PostUpdate, propagate_space_editor_to_studio);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct Selectable;
|
||||
|
||||
#[derive(Component, Clone, Copy)]
|
||||
pub struct Selected;
|
||||
|
||||
#[derive(Resource, Deref, DerefMut, Debug)]
|
||||
pub struct Selection(Entity);
|
||||
|
||||
fn sync_editor_selected(
|
||||
fn propagate_space_editor_to_studio(
|
||||
mut cmd: Commands,
|
||||
q_new_selected: Query<Entity, (Added<Selected>, Without<space_editor::prelude::Selected>)>,
|
||||
q_editor_selected: Query<Entity, With<space_editor::prelude::Selected>>,
|
||||
q_selected: Query<Entity, With<Selected>>,
|
||||
q_added_editor_selected: Query<
|
||||
Entity,
|
||||
(Without<Selected>, Added<space_editor::prelude::Selected>),
|
||||
>,
|
||||
r_selection: Option<Res<Selection>>,
|
||||
q_new_selected: Query<Entity, Added<space_editor::prelude::Selected>>,
|
||||
mut er_deselected: RemovedComponents<space_editor::prelude::Selected>,
|
||||
q_parents: Query<&Parent>,
|
||||
q_selectable: Query<&Selectable>,
|
||||
) {
|
||||
for ent in q_new_selected.iter() {
|
||||
for ed_ent in q_editor_selected.iter() {
|
||||
cmd.entity(ed_ent)
|
||||
.remove::<space_editor::prelude::Selected>();
|
||||
let studio_selection_deselected_in_space_editor = r_selection
|
||||
.as_ref()
|
||||
.map(|selection| {
|
||||
er_deselected
|
||||
.read()
|
||||
.any(|deselected| deselected == ***selection)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
if q_new_selected.is_empty() && studio_selection_deselected_in_space_editor {
|
||||
cmd.remove_resource::<Selection>();
|
||||
} else {
|
||||
let mut new_selected_iter = q_new_selected.iter();
|
||||
let Some(selected) = new_selected_iter.next().and_then(|se_selected| {
|
||||
nearest_selectable_parent(se_selected, &q_parents, &q_selectable)
|
||||
}) else {
|
||||
return;
|
||||
};
|
||||
|
||||
if r_selection.is_none() || **r_selection.unwrap() != selected {
|
||||
cmd.insert_resource(Selection(selected));
|
||||
}
|
||||
cmd.entity(ent).insert(space_editor::prelude::Selected);
|
||||
}
|
||||
|
||||
for ent in q_added_editor_selected.iter() {
|
||||
cmd.entity(ent).insert(Selected);
|
||||
cmd.insert_resource(Selection(ent));
|
||||
|
||||
for ent in q_selected.iter() {
|
||||
cmd.entity(ent).remove::<Selected>();
|
||||
if new_selected_iter.next().is_some() {
|
||||
warn!(
|
||||
"Multiple new entities were selected in Space Editor, but \
|
||||
Storyteller Studio doesn't currently support multiple selections"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,31 +126,11 @@ fn update_selection(
|
||||
mut ev_input: EventReader<InputAction>,
|
||||
q_parents: Query<&Parent>,
|
||||
q_selectable: Query<&Selectable>,
|
||||
q_selected: Query<Entity, With<Selected>>,
|
||||
) {
|
||||
if let Some(event) = ev_select.read().last() {
|
||||
// Clear any existing `Selected` components
|
||||
for ent in q_selected.iter() {
|
||||
cmd.entity(ent).remove::<Selected>();
|
||||
}
|
||||
|
||||
// Find the nearest Selectable parent of the target entity
|
||||
let mut ent = event.target;
|
||||
let ent_selected = loop {
|
||||
if q_selectable.get(ent).is_ok() {
|
||||
break Some(ent);
|
||||
}
|
||||
|
||||
if let Ok(parent) = q_parents.get(ent) {
|
||||
ent = parent.get();
|
||||
} else {
|
||||
break None;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(ent) = ent_selected {
|
||||
if let Some(ent) = nearest_selectable_parent(event.target, &q_parents, &q_selectable) {
|
||||
cmd.insert_resource(Selection(ent));
|
||||
cmd.entity(ent).insert(Selected);
|
||||
} else {
|
||||
cmd.remove_resource::<Selection>();
|
||||
}
|
||||
@@ -152,9 +141,23 @@ fn update_selection(
|
||||
)
|
||||
}) {
|
||||
cmd.remove_resource::<Selection>();
|
||||
}
|
||||
}
|
||||
|
||||
for ent in q_selected.iter() {
|
||||
cmd.entity(ent).remove::<Selected>();
|
||||
fn nearest_selectable_parent(
|
||||
mut target: Entity,
|
||||
q_parents: &Query<&Parent>,
|
||||
q_selectable: &Query<&Selectable>,
|
||||
) -> Option<Entity> {
|
||||
loop {
|
||||
if q_selectable.get(target).is_ok() {
|
||||
break Some(target);
|
||||
}
|
||||
|
||||
if let Ok(parent) = q_parents.get(target) {
|
||||
target = parent.get();
|
||||
} else {
|
||||
break None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -192,7 +195,8 @@ fn finalize_delete(mut cmd: Commands, q: Query<Entity, With<MarkedForDeletion>>)
|
||||
|
||||
fn draw_selection_outline(
|
||||
mut cmd: Commands,
|
||||
q_selected: Query<&Children, With<Selected>>,
|
||||
r_selection: Option<Res<Selection>>,
|
||||
q_children: Query<&Children>,
|
||||
q_meshes: Query<
|
||||
Entity,
|
||||
(
|
||||
@@ -209,8 +213,11 @@ fn draw_selection_outline(
|
||||
cmd.entity(ent).remove::<OutlineBundle>();
|
||||
}
|
||||
|
||||
for children in q_selected.iter() {
|
||||
for &child in children.iter() {
|
||||
if let Some(selected_children) = r_selection
|
||||
.as_ref()
|
||||
.and_then(|selection| q_children.get(***selection).ok())
|
||||
{
|
||||
for &child in selected_children.iter() {
|
||||
if q_meshes.contains(child) {
|
||||
cmd.entity(child).insert(OutlineBundle {
|
||||
outline: OutlineVolume {
|
||||
@@ -233,7 +240,7 @@ mod wasm {
|
||||
};
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use super::{Selected, Selection};
|
||||
use super::Selection;
|
||||
use crate::wasm::{self, FromStr, StaticBuffer, WasmMessageBuffer};
|
||||
|
||||
#[wasm_bindgen(typescript_custom_section)]
|
||||
@@ -273,7 +280,6 @@ declare global {
|
||||
pub(super) fn update_selection_from_frontend(mut cmd: Commands) {
|
||||
if let Some(entity) = SELECTION_REQUEST.take_message() {
|
||||
cmd.insert_resource(Selection(entity));
|
||||
cmd.entity(entity).insert(Selected);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+28
-26
@@ -19,7 +19,7 @@ use space_editor::{
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
use crate::{
|
||||
interaction::Selected,
|
||||
interaction::Selection,
|
||||
wasm::{self, StaticBuffer, WasmMessageBuffer},
|
||||
};
|
||||
|
||||
@@ -69,7 +69,8 @@ impl Plugin for InspectorPlugin {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_inspect_by_select_change::<Handle<StandardMaterial>>,
|
||||
update_inspect_by_select_change::<Handle<StandardMaterial>>
|
||||
.run_if(resource_changed_or_removed::<Selection>()),
|
||||
update_inspect_by_remove::<Handle<StandardMaterial>>,
|
||||
update_inspect_by_change::<Handle<StandardMaterial>>,
|
||||
notify_asset_change_system::<StandardMaterial>,
|
||||
@@ -125,7 +126,8 @@ impl AppInspectable for App {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_inspect_by_select_change::<T>,
|
||||
update_inspect_by_select_change::<T>
|
||||
.run_if(resource_changed_or_removed::<Selection>()),
|
||||
update_inspect_by_remove::<T>,
|
||||
update_inspect_by_change::<T>,
|
||||
)
|
||||
@@ -158,7 +160,8 @@ impl AppInspectable for App {
|
||||
.add_systems(
|
||||
Update,
|
||||
(
|
||||
update_inspect_by_select_change::<Handle<T>>,
|
||||
update_inspect_by_select_change::<Handle<T>>
|
||||
.run_if(resource_changed_or_removed::<Selection>()),
|
||||
update_inspect_by_remove::<Handle<T>>,
|
||||
update_inspect_by_change::<Handle<T>>,
|
||||
notify_asset_change_system::<T>,
|
||||
@@ -227,12 +230,10 @@ fn update_field_by_ui(world: &mut World) {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut selected_query = unsafe_world
|
||||
.world_mut()
|
||||
.query_filtered::<Entity, With<Selected>>();
|
||||
let Ok(entity) = selected_query.get_single(unsafe_world.world_mut()) else {
|
||||
let Some(selection) = unsafe_world.world_mut().get_resource::<Selection>() else {
|
||||
return;
|
||||
};
|
||||
let entity = **selection;
|
||||
|
||||
let getters = unsafe_world.get_resource_mut::<InspectGetters>().unwrap();
|
||||
|
||||
@@ -320,16 +321,8 @@ pub struct EntityDescription {
|
||||
|
||||
fn update_inspect_by_select_change<T: Component + Reflect + GetTypeRegistration>(
|
||||
mut ev_inspect: EventWriter<UpdateInspect<T>>,
|
||||
q_added: Query<(), Added<Selected>>,
|
||||
q_removed: RemovedComponents<Selected>,
|
||||
) {
|
||||
if !q_added.is_empty() {
|
||||
ev_inspect.send(UpdateInspect::<T>::default());
|
||||
}
|
||||
|
||||
if !q_removed.is_empty() {
|
||||
ev_inspect.send(UpdateInspect::<T>::default());
|
||||
}
|
||||
ev_inspect.send(UpdateInspect::<T>::default());
|
||||
}
|
||||
|
||||
fn update_inspect_by_remove<T: Component + Reflect + GetTypeRegistration>(
|
||||
@@ -342,16 +335,20 @@ fn update_inspect_by_remove<T: Component + Reflect + GetTypeRegistration>(
|
||||
}
|
||||
|
||||
fn update_inspect_by_change<T: Component + Reflect + GetTypeRegistration>(
|
||||
r_selection: Option<Res<Selection>>,
|
||||
mut ev_inspect: EventWriter<UpdateInspect<T>>,
|
||||
q_t_changed: Query<&T, (With<Selected>, Changed<T>)>,
|
||||
q_t_changed: Query<&T, Changed<T>>,
|
||||
) {
|
||||
if q_t_changed.iter().next().is_some() {
|
||||
ev_inspect.send(UpdateInspect::<T>::default());
|
||||
if let Some(selection) = r_selection {
|
||||
if q_t_changed.contains(**selection) {
|
||||
ev_inspect.send(UpdateInspect::<T>::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn auto_serialize_system<T: Component + Reflect + GetTypeRegistration>(
|
||||
q_t: Query<&T, With<Selected>>,
|
||||
r_selection: Option<Res<Selection>>,
|
||||
q_t: Query<&T>,
|
||||
mut description: ResMut<EntityDescription>,
|
||||
registry: Res<AppTypeRegistry>,
|
||||
mut ev_inspect: EventReader<UpdateInspect<T>>,
|
||||
@@ -363,7 +360,7 @@ fn auto_serialize_system<T: Component + Reflect + GetTypeRegistration>(
|
||||
|
||||
ev_inspect.clear();
|
||||
|
||||
if let Some(t) = q_t.iter().next() {
|
||||
if let Some(t) = r_selection.and_then(|selection| q_t.get(**selection).ok()) {
|
||||
let registry = registry.read();
|
||||
let json = serde_json::to_value(ReflectSerializer::new(t, ®istry)).unwrap();
|
||||
info!("json: {:?}", json.to_string());
|
||||
@@ -384,12 +381,16 @@ fn auto_serialize_system<T: Component + Reflect + GetTypeRegistration>(
|
||||
}
|
||||
|
||||
fn notify_asset_change_system<T: Asset>(
|
||||
r_selection: Option<Res<Selection>>,
|
||||
mut ev_inspect: EventWriter<UpdateInspect<Handle<T>>>,
|
||||
mut asset_changing: EventReader<AssetEvent<T>>,
|
||||
mut selected: Query<&Handle<T>, With<Selected>>,
|
||||
q_handles: Query<&Handle<T>>,
|
||||
) {
|
||||
for ev in asset_changing.read() {
|
||||
for handle in selected.iter_mut() {
|
||||
if let Some(handle) = r_selection
|
||||
.as_ref()
|
||||
.and_then(|selection| q_handles.get(***selection).ok())
|
||||
{
|
||||
if ev.is_modified(handle.id()) {
|
||||
ev_inspect.send(UpdateInspect::<Handle<T>>::default());
|
||||
}
|
||||
@@ -398,7 +399,8 @@ fn notify_asset_change_system<T: Asset>(
|
||||
}
|
||||
|
||||
fn auto_serialize_asset<T: Asset + Reflect + GetTypeRegistration>(
|
||||
q_t: Query<&Handle<T>, With<Selected>>,
|
||||
r_selection: Option<Res<Selection>>,
|
||||
q_t: Query<&Handle<T>>,
|
||||
mut description: ResMut<EntityDescription>,
|
||||
registry: Res<AppTypeRegistry>,
|
||||
mut ev_inspect: EventReader<UpdateInspect<Handle<T>>>,
|
||||
@@ -411,7 +413,7 @@ fn auto_serialize_asset<T: Asset + Reflect + GetTypeRegistration>(
|
||||
|
||||
ev_inspect.clear();
|
||||
|
||||
if let Some(handle) = q_t.iter().next() {
|
||||
if let Some(handle) = r_selection.and_then(|selection| q_t.get(**selection).ok()) {
|
||||
if let Some(asset) = assets.get(handle) {
|
||||
let registry = registry.read();
|
||||
info!(
|
||||
|
||||
Reference in New Issue
Block a user