Newer
Older
}
if let Some(children) =
world.entity(entity.0).get::<KChildren>().cloned()
{
if let Some(mut entity) = world.get_entity_mut(*target_entity) {
entity.insert(children);
}
}
}
// Mark nodes left as dirty.
for child in widget_context.child_iter(*entity) {
if let Some(mut entity_commands) = world.get_entity_mut(child.0) {
entity_commands.insert(DirtyNode);
}
}
let children = if let Ok(tree) = tree.read() {
tree.child_iter(*entity).collect::<Vec<_>>()
} else {
vec![]
};
world,
tree,
layout_cache,
systems,
children,
context_entities,
focus_tree,
clone_systems,
cloned_widget_entities,
widget_state,
} else {
// In this case the entity we are trying to process no longer exists.
// The approach taken here removes said entities from the tree.
// If the child exists as a child of one of the children we do not need to remove it.
// TODO: This is kinda of expensive we should think of a way of making this faster..
let mut contained_in_children = false;
if let Ok(tree) = tree.read() {
if let Ok(order_tree) = order_tree.try_read() {
if let Some(parent) = order_tree.parent(*entity) {
'outside_loop: for sibling in order_tree.child_iter(parent) {
for child in tree.down_iter_at(sibling, true) {
if let Some(entity_ref) = world.get_entity(child.0) {
if let Some(children) = entity_ref.get::<KChildren>() {
if children.contains_entity(entity.0) {
trace!("Caught an entity that was marked as deleted but wasn't! {:?}", entity.0);
// Don't despawn changed entity because it exists as a child passed via props
contained_in_children = true;
break 'outside_loop;
}
}
}
}
}
if !contained_in_children {
if let Ok(mut tree) = tree.write() {
if let Ok(mut order_tree) = order_tree.try_write() {
log::trace!("Removing dangling entity! {:?}", entity.0.index());
order_tree.remove(*entity);
tree.remove(*entity);
if let Some(mut entity_mut) = world.get_entity_mut(entity.0) {
entity_mut.remove_parent();
entity_mut.remove::<bevy::prelude::Children>();
entity_mut.despawn();
// Remove state entity
if let Some(state_entity) = widget_state.remove(entity.0) {
if let Some(mut entity_mut) = world.get_entity_mut(state_entity) {
entity_mut.remove_parent();
entity_mut.despawn_recursive();
}
}
}
if let Some(entity_ref) = world.get_entity(entity.0) {
if entity_ref.contains::<Focusable>() {
if let Ok(tree) = tree.try_read() {
focus_tree.add(*entity, &tree);
}
}
}
}
}
fn update_widget(
systems: &mut WidgetSystems,
focus_tree: &FocusTree,
world: &mut World,
entity: WrappedIndex,
widget_type: String,
mut widget_context: KayakWidgetContext,
clone_systems: &Arc<RwLock<EntityCloneSystems>>,
widget_state: &WidgetState,
_unique_ids: &DashMap<Entity, DashMap<String, Entity>>,
_unique_ids_parents: &DashMap<Entity, Entity>,
// Check if we should update this widget
let should_rerender = {
let old_props_entity = {
let old_parent_entity = if let Ok(tree) = tree.try_read() {
if let Some(parent_entity) = tree.get_parent(entity) {
cloned_widget_entities
.get(&parent_entity.0)
.map(|v| *v.value())
}
} else {
None
};
if let Some(entity) = cloned_widget_entities.get(&entity.0).map(|v| *v.value()) {
if let Some(possible_entity) = world.get_entity(entity) {
let target = possible_entity.id();
cloned_widget_entities.insert(entity, target);
target
} else {
let target = world.spawn_empty().insert(PreviousWidget).id();
if let Some(parent_id) = old_parent_entity {
target
}
} else {
let target = world.spawn_empty().insert(PreviousWidget).id();
if let Some(parent_id) = old_parent_entity {
if let Some(mut entity_mut) = world.get_entity_mut(parent_id) {
entity_mut.add_child(target);
}
}
cloned_widget_entities.insert(entity.0, target);
target
}
};
let widget_update_system = &mut systems
.get_mut(&widget_type)
.unwrap_or_else(|| {
panic!(
"Wasn't able to find render/update systems for widget: {}!",
widget_type
)
})
let old_tick = widget_update_system.get_last_change_tick();
// Insert context as a bevy resource.
world.insert_resource(widget_context);
let should_rerender = widget_update_system.run((entity.0, old_props_entity), world);
let new_tick = widget_update_system.get_last_change_tick();
new_ticks.insert(widget_type.clone(), new_tick);
widget_update_system.set_last_change_tick(old_tick);
widget_update_system.apply_buffers(world);
// Extract context
widget_context = world.remove_resource::<KayakWidgetContext>().unwrap();
if should_rerender {
if let Some(target_entity) = cloned_widget_entities.get(&entity.0).map(|v| *v.value()) {
if let Ok(clone_systems) = clone_systems.try_read() {
for s in clone_systems.0.iter() {
s.0(world, target_entity, entity.0);
s.1(world, target_entity, entity.0, widget_state);
if let Some(styles) = world.entity(entity.0).get::<KStyle>().cloned() {
if let Some(mut entity) = world.get_entity_mut(target_entity) {
entity.insert(styles);
}
if let Some(styles) =
world.entity(entity.0).get::<ComputedStyles>().cloned()
{
if let Some(mut entity) = world.get_entity_mut(target_entity) {
entity.insert(styles);
}
if let Some(children) = world.entity(entity.0).get::<KChildren>().cloned() {
if let Some(mut entity) = world.get_entity_mut(target_entity) {
entity.insert(children);
if let Some(widget_name) =
world.entity(entity.0).get::<WidgetName>().cloned()
{
if let Some(mut entity) = world.get_entity_mut(target_entity) {
entity.insert(widget_name);
}
}
}
}
}
should_rerender
};
if !should_rerender {
return (widget_context.take(), false);
}
if let Ok(tree) = tree.try_read() {
log::trace!(
"Re-rendering: {:?} {:?}, parent: {:?}",
&widget_type,
tree.parent(entity)
.unwrap_or(WrappedIndex(Entity::from_raw(99999)))
.0
// Before rendering widget we need to advance the indices correctly..
if let Some(children) = world.get::<KChildren>(entity.0) {
let child_count = children.len();
widget_context.index.insert(entity.0, 0);
log::trace!(
"Advancing children for: {:?} by: {:?}",
entity.0.index(),
child_count
);
// Remove children from previous render.
widget_context.remove_children(previous_children);
let widget_render_system = &mut systems.get_mut(&widget_type).unwrap().1;
let old_tick = widget_render_system.get_last_change_tick();
world.insert_resource(widget_context.clone());
world.insert_resource(focus_tree.clone());
should_update_children = widget_render_system.run(entity.0, world);
let new_tick = widget_render_system.get_last_change_tick();
widget_render_system.set_last_change_tick(old_tick);
widget_render_system.apply_buffers(world);
world.remove_resource::<KayakWidgetContext>();
world.remove_resource::<FocusTree>();
}
let widget_context = widget_context.take();
let mut command_queue = CommandQueue::default();
let mut commands = Commands::new(&mut command_queue, world);
commands.entity(entity.0).remove::<Mounted>();
// Always mark widget dirty if it's re-rendered.
// Mark node as needing a recalculation of rendering/layout.
commands.entity(entity.0).insert(DirtyNode);
(widget_context, should_update_children)
}
/// The default Kayak Context plugin
/// Creates systems and resources for kayak.
pub struct KayakContextPlugin;
#[derive(Resource)]
pub struct CustomEventReader<T: bevy::ecs::event::Event>(pub ManualEventReader<T>);
impl Plugin for KayakContextPlugin {
fn build(&self, app: &mut App) {
app.insert_resource(WindowSize::default())
.insert_resource(CustomEventReader(ManualEventReader::<
bevy::window::CursorMoved,
>::default()))
.insert_resource(CustomEventReader(ManualEventReader::<
bevy::input::mouse::MouseButtonInput,
>::default()))
.insert_resource(CustomEventReader(ManualEventReader::<
bevy::input::mouse::MouseWheel,
>::default()))
.insert_resource(CustomEventReader(ManualEventReader::<
bevy::window::ReceivedCharacter,
>::default()))
.insert_resource(CustomEventReader(ManualEventReader::<
bevy::input::keyboard::KeyboardInput,
>::default()))
.add_plugin(crate::camera::KayakUICameraPlugin)
.add_plugin(crate::render::BevyKayakUIRenderPlugin)
.add_system(crate::input::process_events.in_base_set(CoreSet::Update))
.add_system(update_widgets_sys.in_base_set(CoreSet::PostUpdate))
.add_system(
calculate_ui
.after(update_widgets_sys)
.in_base_set(CoreSet::PostUpdate),
)
.add_system(crate::window_size::update_window_size);
// Register reflection types.
// A bit annoying..
app //.register_type::<Node>()
.register_type::<ComputedStyles>()
.register_type::<KChildren>()
.register_type::<crate::layout::Rect>()
.register_type::<crate::node::Node>()
.register_type::<WidgetName>()
.register_type::<StyleProp<Color>>()
.register_type::<StyleProp<Corner<f32>>>()
.register_type::<StyleProp<Edge<f32>>>()
.register_type::<StyleProp<Units>>()
.register_type::<StyleProp<KCursorIcon>>()
.register_type::<StyleProp<String>>()
.register_type::<StyleProp<f32>>()
.register_type::<StyleProp<LayoutType>>()
.register_type::<StyleProp<Edge<Units>>>()
.register_type::<StyleProp<PointerEvents>>()
.register_type::<StyleProp<KPositionType>>()
.register_type::<StyleProp<RenderCommand>>()
.register_type::<StyleProp<i32>>();
}
}
fn calculate_ui(world: &mut World) {
let mut context_data = Vec::new();
query_world::<Query<(Entity, &mut EventDispatcher, &mut KayakRootContext)>, _, _>(
|mut query| {
for (entity, mut event_dispatcher, mut kayak_root_context) in query.iter_mut() {
context_data.push((
entity,
std::mem::take(&mut *event_dispatcher),
std::mem::take(&mut *kayak_root_context),
));
}
},
world,
);
for (entity, event_dispatcher, mut context) in context_data.drain(..) {
let mut node_system = IntoSystem::into_system(calculate_nodes);
node_system.initialize(world);
let mut layout_system = IntoSystem::into_system(calculate_layout);
layout_system.initialize(world);
context = node_system.run(context, world);
node_system.apply_buffers(world);
context = layout_system.run(context, world);
layout_system.apply_buffers(world);
LayoutEventDispatcher::dispatch(&mut context, world);
}
if event_dispatcher.hovered.is_none() {
context.current_cursor = CursorIcon::Default;
} else {
let hovered = event_dispatcher.hovered.unwrap();
if let Some(entity) = world.get_entity(hovered.0) {
if let Some(node) = entity.get::<crate::node::Node>() {
let icon = node.resolved_styles.cursor.resolve();
context.current_cursor = icon.0;
}
}
if let Ok(mut window) = world
.query_filtered::<&mut Window, With<PrimaryWindow>>()
.get_single_mut(world)
{
window.cursor.icon = context.current_cursor;
world.entity_mut(entity).insert((event_dispatcher, context));
}
/// A simple component that stores the type name of a widget
/// This is used by Kayak in order to find out which systems to run.
#[derive(Component, Reflect, Debug, Clone, PartialEq, Eq)]
#[reflect(Component)]
impl Default for WidgetName {
fn default() -> Self {
log::warn!("You did not specify a widget name for a widget!");
Self("NO_NAME".to_string())
}
}
impl From<String> for WidgetName {
fn from(value: String) -> Self {
WidgetName(value)
}
}
impl From<WidgetName> for String {
fn from(val: WidgetName) -> Self {
val.0