use crate::scene::EntityInfo;
use crate::{
command::{make_command, Command, CommandGroup, SetPropertyCommand},
fyrox::{
core::{pool::Handle, reflect::Reflect, some_or_return},
engine::Engine,
graph::{SceneGraph, SceneGraphNode},
gui::{inspector::PropertyChanged, UiNode, UserInterface},
scene::SceneContainer,
},
message::MessageSender,
scene::{commands::ChangeSelectionCommand, controller::SceneController, SelectionContainer},
ui_scene::{
commands::{
graph::DeleteWidgetsSubGraphCommand, widget::RevertWidgetPropertyCommand,
UiSceneContext,
},
UiScene,
},
};
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct UiSelection {
pub widgets: Vec<Handle<UiNode>>,
}
impl SelectionContainer for UiSelection {
fn len(&self) -> usize {
self.widgets.len()
}
fn first_selected_entity(
&self,
controller: &dyn SceneController,
_scenes: &SceneContainer,
callback: &mut dyn FnMut(EntityInfo),
) {
let ui_scene = some_or_return!(controller.downcast_ref::<UiScene>());
if let Some(first) = self.widgets.first() {
if let Ok(node) = ui_scene.ui.try_get_node(*first) {
(callback)(EntityInfo {
entity: node as &dyn Reflect,
has_inheritance_parent: node.has_inheritance_parent(),
read_only: false,
})
}
}
}
fn on_property_changed(
&mut self,
controller: &mut dyn SceneController,
args: &PropertyChanged,
_engine: &mut Engine,
sender: &MessageSender,
) {
let ui_scene = some_or_return!(controller.downcast_ref::<UiScene>());
let group = self
.widgets
.iter()
.filter_map(|&node_handle| {
let node = ui_scene.ui.try_get_node(node_handle).ok()?;
if args.is_inheritable() {
if node.resource().is_some() {
Some(Command::new(RevertWidgetPropertyCommand::new(
args.path(),
node_handle,
)))
} else {
None
}
} else {
make_command(args, move |ctx| {
ctx.get_mut::<UiSceneContext>()
.ui
.try_get_node_mut(node_handle)
.ok()
.map(|n| n as &mut dyn Reflect)
})
}
})
.collect::<Vec<_>>();
sender.do_command_group_with_inheritance(group, args);
}
fn paste_property(&mut self, path: &str, value: &dyn Reflect, sender: &MessageSender) {
let group = self
.widgets
.iter()
.filter_map(|&node_handle| {
value.try_clone_box().map(|value| {
Command::new(SetPropertyCommand::new(
path.to_string(),
value,
move |ctx| {
ctx.get_mut::<UiSceneContext>()
.ui
.try_get_node_mut(node_handle)
.ok()
.map(|n| n as &mut dyn Reflect)
},
))
})
})
.collect::<Vec<_>>();
sender.do_command_group(group);
}
fn provide_docs(&self, controller: &dyn SceneController, _engine: &Engine) -> Option<String> {
let ui_scene = controller.downcast_ref::<UiScene>()?;
self.widgets.first().and_then(|h| {
ui_scene
.ui
.try_get_node(*h)
.ok()
.map(|n| n.doc().to_string())
})
}
}
impl UiSelection {
pub fn single_or_empty(node: Handle<UiNode>) -> Self {
if node.is_none() {
Self {
widgets: Default::default(),
}
} else {
Self {
widgets: vec![node],
}
}
}
pub fn is_empty(&self) -> bool {
self.widgets.is_empty()
}
pub fn len(&self) -> usize {
self.widgets.len()
}
pub fn insert_or_exclude(&mut self, handle: Handle<UiNode>) {
if let Some(position) = self.widgets.iter().position(|&h| h == handle) {
self.widgets.remove(position);
} else {
self.widgets.push(handle);
}
}
pub fn selection_to_delete(&self, ui: &UserInterface) -> UiSelection {
let mut selection = self.clone();
if let Some(root_position) = selection.widgets.iter().position(|&n| n == ui.root()) {
selection.widgets.remove(root_position);
}
selection
}
pub fn root_widgets(&self, ui: &UserInterface) -> Vec<Handle<UiNode>> {
fn is_descendant_of(
handle: Handle<UiNode>,
other: Handle<UiNode>,
ui: &UserInterface,
) -> bool {
for &child in ui.node(other).children() {
if child == handle {
return true;
}
let inner = is_descendant_of(handle, child, ui);
if inner {
return true;
}
}
false
}
let mut root_widgets = Vec::new();
for &node in self.widgets.iter() {
let mut descendant = false;
for &other_node in self.widgets.iter() {
if is_descendant_of(node, other_node, ui) {
descendant = true;
break;
}
}
if !descendant {
root_widgets.push(node);
}
}
root_widgets
}
pub fn make_deletion_command(&self, ui: &UserInterface) -> Command {
let selection = self.selection_to_delete(ui);
let mut command_group = CommandGroup::from(vec![Command::new(
ChangeSelectionCommand::new(Default::default()),
)]);
let root_nodes = selection.root_widgets(ui);
for root_node in root_nodes {
command_group.push(DeleteWidgetsSubGraphCommand::new(root_node));
}
Command::new(command_group)
}
}