Skip to content
Snippets Groups Projects
Commit 6cfb52bb authored by StarToaster's avatar StarToaster
Browse files

Updates, Props, State, and Context version 2.0!

parent 73554da6
No related branches found
No related tags found
No related merge requests found
Showing
with 810 additions and 399 deletions
...@@ -17,6 +17,7 @@ kayak_font = { path = "./kayak_font" } ...@@ -17,6 +17,7 @@ kayak_font = { path = "./kayak_font" }
morphorm = { git = "https://github.com/geom3trik/morphorm", rev = "1243152d4cebea46fd3e5098df26402c73acae91" } morphorm = { git = "https://github.com/geom3trik/morphorm", rev = "1243152d4cebea46fd3e5098df26402c73acae91" }
kayak_ui_macros = { path = "./kayak_ui_macros" } kayak_ui_macros = { path = "./kayak_ui_macros" }
indexmap = "1.9" indexmap = "1.9"
log = "0.4"
[dev-dependencies] [dev-dependencies]
fastrand = "1.8" fastrand = "1.8"
......
This diff is collapsed.
use bevy::prelude::*; use bevy::prelude::*;
use kayak_ui::prelude::*; use kayak_ui::prelude::{widgets::*, *};
#[derive(Component, Default)] #[derive(Debug, Component, Default, Clone, PartialEq)]
pub struct MyWidget2 {
bar: u32,
}
fn my_widget_2_update(
In((_widget_context, entity)): In<(WidgetContext, Entity)>,
query: Query<&MyWidget2, Or<(Added<MyWidget2>, Changed<MyWidget2>)>>,
) -> bool {
if let Ok(my_widget2) = query.get(entity) {
dbg!(my_widget2.bar);
}
true
}
impl Widget for MyWidget2 {}
#[derive(Component, Default)]
pub struct MyWidget { pub struct MyWidget {
pub foo: u32, pub foo: u32,
} }
fn my_widget_1_update( fn my_widget_1_render(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((_widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut _commands: Commands,
my_resource: Res<MyResource>, query: Query<&MyWidget>,
mut query: Query<&mut MyWidget>,
) -> bool { ) -> bool {
if my_resource.is_changed() { if let Ok(my_widget) = query.get(entity) {
if let Ok(mut my_widget) = query.get_mut(entity) { dbg!(my_widget.foo);
my_widget.foo = my_resource.0;
dbg!(my_widget.foo);
let my_child = MyWidget2 { bar: my_widget.foo };
let should_update = my_widget.foo == my_child.bar;
let child_id = commands
.spawn((my_child, MyWidget2::default().get_name()))
.id();
widget_context.add_widget(Some(entity), child_id);
return should_update;
}
} }
false true
} }
impl Widget for MyWidget {} impl Widget for MyWidget {}
impl WidgetProps for MyWidget {}
#[derive(Resource)]
pub struct MyResource(pub u32);
fn startup(mut commands: Commands) { fn startup(mut commands: Commands) {
let mut context = Context::new(); let mut context = Context::new();
context.add_widget_system(MyWidget::default().get_name(), my_widget_1_update); context.add_widget_system(
context.add_widget_system(MyWidget2::default().get_name(), my_widget_2_update); MyWidget::default().get_name(),
widget_update::<MyWidget, EmptyState>,
my_widget_1_render,
);
context.add_widget_data::<MyWidget, EmptyState>();
let app_entity = commands.spawn_empty().id();
let mut children = KChildren::default();
let entity = commands let entity = commands
.spawn(( .spawn((
MyWidget { foo: 0 }, MyWidget { foo: 0 },
...@@ -65,13 +39,27 @@ fn startup(mut commands: Commands) { ...@@ -65,13 +39,27 @@ fn startup(mut commands: Commands) {
MyWidget::default().get_name(), MyWidget::default().get_name(),
)) ))
.id(); .id();
context.add_widget(None, entity); children.add(entity);
commands.entity(app_entity).insert(KayakAppBundle {
children,
..KayakAppBundle::default()
});
context.add_widget(None, app_entity);
commands.insert_resource(context); commands.insert_resource(context);
} }
fn update_resource(keyboard_input: Res<Input<KeyCode>>, mut my_resource: ResMut<MyResource>) { // Note this example shows prop changing not state changing which is quite different.
// For state changes please see simple_state example.
fn update_resource(
keyboard_input: Res<Input<KeyCode>>,
mut query: Query<&mut MyWidget, Without<PreviousWidget>>,
) {
if keyboard_input.just_pressed(KeyCode::Space) { if keyboard_input.just_pressed(KeyCode::Space) {
my_resource.0 += 1; for mut my_widget in query.iter_mut() {
my_widget.foo += 1;
}
} }
} }
...@@ -79,7 +67,7 @@ fn main() { ...@@ -79,7 +67,7 @@ fn main() {
App::new() App::new()
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(ContextPlugin) .add_plugin(ContextPlugin)
.insert_resource(MyResource(1)) .add_plugin(KayakWidgets)
.add_startup_system(startup) .add_startup_system(startup)
.add_system(update_resource) .add_system(update_resource)
.run() .run()
......
use bevy::{ use bevy::{
diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin}, diagnostic::{FrameTimeDiagnosticsPlugin, LogDiagnosticsPlugin},
prelude::{ prelude::{
App as BevyApp, AssetServer, Bundle, Changed, Color, Commands, Component, Entity, In, App as BevyApp, AssetServer, Bundle, Color, Commands, Component, Entity, In, Query, Res,
Query, Res, ResMut, Vec2, ResMut, Vec2,
}, },
DefaultPlugins, DefaultPlugins,
}; };
use kayak_ui::prelude::{widgets::*, KStyle, *}; use kayak_ui::prelude::{widgets::*, KStyle, *};
use morphorm::{PositionType, Units}; use morphorm::{PositionType, Units};
#[derive(Component, Default)] #[derive(Component, Default, Clone, PartialEq)]
pub struct MyQuad { pub struct MyQuad {
pos: Vec2, pos: Vec2,
pub size: Vec2, pub size: Vec2,
...@@ -18,28 +18,56 @@ pub struct MyQuad { ...@@ -18,28 +18,56 @@ pub struct MyQuad {
fn my_quad_update( fn my_quad_update(
In((_widget_context, entity)): In<(WidgetContext, Entity)>, In((_widget_context, entity)): In<(WidgetContext, Entity)>,
mut query: Query<(&MyQuad, &mut KStyle), Changed<MyQuad>>, mut query: Query<(&MyQuad, &mut KStyle, &mut OnEvent)>,
) -> bool { ) -> bool {
if let Ok((quad, mut style)) = query.get_mut(entity) { if let Ok((quad, mut style, mut on_event)) = query.get_mut(entity) {
style.render_command = StyleProp::Value(RenderCommand::Quad); if style.render_command.resolve() != RenderCommand::Quad {
style.position_type = StyleProp::Value(PositionType::SelfDirected); style.render_command = StyleProp::Value(RenderCommand::Quad);
style.left = StyleProp::Value(Units::Pixels(quad.pos.x)); style.position_type = StyleProp::Value(PositionType::SelfDirected);
style.top = StyleProp::Value(Units::Pixels(quad.pos.y)); style.left = StyleProp::Value(Units::Pixels(quad.pos.x));
style.width = StyleProp::Value(Units::Pixels(quad.size.x)); style.top = StyleProp::Value(Units::Pixels(quad.pos.y));
style.height = StyleProp::Value(Units::Pixels(quad.size.y)); style.width = StyleProp::Value(Units::Pixels(quad.size.x));
style.background_color = StyleProp::Value(quad.color); style.height = StyleProp::Value(Units::Pixels(quad.size.y));
return true; style.background_color = StyleProp::Value(quad.color);
}
*on_event = OnEvent::new(
move |In((event_dispatcher_context, _, event, entity)): In<(
EventDispatcherContext,
WidgetState,
Event,
Entity,
)>,
mut query: Query<(&mut KStyle, &MyQuad)>| {
match event.event_type {
EventType::MouseIn(..) => {
if let Ok((mut styles, _)) = query.get_mut(entity) {
styles.background_color = StyleProp::Value(Color::WHITE);
}
}
EventType::MouseOut(..) => {
if let Ok((mut styles, my_quad)) = query.get_mut(entity) {
styles.background_color = StyleProp::Value(my_quad.color);
}
}
_ => {}
}
(event_dispatcher_context, event)
},
);
} }
false true
} }
impl Widget for MyQuad {} impl Widget for MyQuad {}
impl WidgetProps for MyQuad {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct MyQuadBundle { pub struct MyQuadBundle {
my_quad: MyQuad, my_quad: MyQuad,
styles: KStyle, styles: KStyle,
on_event: OnEvent,
widget_name: WidgetName, widget_name: WidgetName,
} }
...@@ -48,6 +76,7 @@ impl Default for MyQuadBundle { ...@@ -48,6 +76,7 @@ impl Default for MyQuadBundle {
Self { Self {
my_quad: Default::default(), my_quad: Default::default(),
styles: KStyle::default(), styles: KStyle::default(),
on_event: OnEvent::default(),
widget_name: MyQuad::default().get_name(), widget_name: MyQuad::default().get_name(),
} }
} }
...@@ -63,7 +92,11 @@ fn startup( ...@@ -63,7 +92,11 @@ fn startup(
commands.spawn(UICameraBundle::new()); commands.spawn(UICameraBundle::new());
let mut widget_context = Context::new(); let mut widget_context = Context::new();
widget_context.add_widget_system(MyQuad::default().get_name(), my_quad_update); widget_context.add_widget_system(
MyQuad::default().get_name(),
widget_update::<MyQuad, EmptyState>,
my_quad_update,
);
let parent_id = None; let parent_id = None;
rsx! { rsx! {
......
use bevy::{ use bevy::{
prelude::{ prelude::{
App as BevyApp, AssetServer, Bundle, Changed, Commands, Component, Entity, In, Query, Res, App as BevyApp, AssetServer, Bundle, Commands, Component, Entity, In, Query, Res, ResMut,
ResMut, Vec2, Vec2,
}, },
DefaultPlugins, DefaultPlugins,
}; };
use kayak_ui::prelude::{widgets::*, *}; use kayak_ui::prelude::{widgets::*, *};
#[derive(Component, Default)] #[derive(Component, Default, PartialEq, Clone)]
struct CurrentCount(pub u32); struct CurrentCount;
impl Widget for CurrentCount {} impl Widget for CurrentCount {}
impl WidgetProps for CurrentCount {}
#[derive(Component, Default, PartialEq, Clone)]
struct CurrentCountState {
foo: u32,
}
#[derive(Bundle)] #[derive(Bundle)]
struct CurrentCountBundle { struct CurrentCountBundle {
...@@ -29,18 +35,20 @@ impl Default for CurrentCountBundle { ...@@ -29,18 +35,20 @@ impl Default for CurrentCountBundle {
} }
} }
fn current_count_update( fn current_count_render(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
query: Query<&CurrentCount, Changed<CurrentCount>>, query: Query<&CurrentCountState>,
) -> bool { ) -> bool {
if let Ok(current_count) = query.get(entity) { let state_entity =
widget_context.use_state(&mut commands, entity, CurrentCountState::default());
if let Ok(current_count) = query.get(state_entity) {
let parent_id = Some(entity); let parent_id = Some(entity);
rsx! { rsx! {
<TextWidgetBundle <TextWidgetBundle
text={ text={
TextProps { TextProps {
content: format!("Current Count: {}", current_count.0).into(), content: format!("Current Count: {}", current_count.foo).into(),
size: 16.0, size: 16.0,
line_height: Some(40.0), line_height: Some(40.0),
..Default::default() ..Default::default()
...@@ -66,7 +74,12 @@ fn startup( ...@@ -66,7 +74,12 @@ fn startup(
let mut widget_context = Context::new(); let mut widget_context = Context::new();
let parent_id = None; let parent_id = None;
widget_context.add_widget_system(CurrentCount::default().get_name(), current_count_update); widget_context.add_widget_data::<CurrentCount, CurrentCountState>();
widget_context.add_widget_system(
CurrentCount::default().get_name(),
widget_update::<CurrentCount, CurrentCountState>,
current_count_render,
);
rsx! { rsx! {
<KayakAppBundle> <KayakAppBundle>
<WindowBundle <WindowBundle
...@@ -81,14 +94,14 @@ fn startup( ...@@ -81,14 +94,14 @@ fn startup(
<CurrentCountBundle id={"current_count_entity"} /> <CurrentCountBundle id={"current_count_entity"} />
<KButtonBundle <KButtonBundle
on_event={OnEvent::new( on_event={OnEvent::new(
move |In((event_dispatcher_context, event, _entity)): In<(EventDispatcherContext, Event, Entity)>, move |In((event_dispatcher_context, widget_state, event, _entity)): In<(EventDispatcherContext, WidgetState, Event, Entity)>,
mut query: Query<&mut CurrentCount>| { mut query: Query<&mut CurrentCountState>| {
match event.event_type { match event.event_type {
EventType::Click(..) => { EventType::Click(..) => {
if let Ok(mut current_count) = if let Some(state_entity) = widget_state.get(current_count_entity) {
query.get_mut(current_count_entity) if let Ok(mut current_count) = query.get_mut(state_entity) {
{ current_count.foo += 1;
current_count.0 += 1; }
} }
} }
_ => {} _ => {}
......
use bevy::prelude::{ use bevy::prelude::{Bundle, Color, Commands, Component, Entity, In, Query};
Bundle, ChangeTrackers, Changed, Color, Commands, Component, Entity, In, ParamSet, Query,
};
use kayak_ui::prelude::{ use kayak_ui::prelude::{
widgets::BackgroundBundle, Edge, KChildren, KStyle, StyleProp, Units, Widget, WidgetContext, widgets::BackgroundBundle, Edge, KChildren, KStyle, StyleProp, Units, Widget, WidgetContext,
WidgetName, WidgetName, WidgetProps,
}; };
use kayak_ui_macros::rsx; use kayak_ui_macros::rsx;
use crate::tab_context::TabContext; use crate::tab_context::TabContext;
#[derive(Component, Default)] #[derive(Component, Default, PartialEq, Clone)]
pub struct Tab { pub struct Tab {
pub index: usize, pub index: usize,
} }
impl Widget for Tab {} impl Widget for Tab {}
impl WidgetProps for Tab {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct TabBundle { pub struct TabBundle {
pub tab: Tab, pub tab: Tab,
pub styles: KStyle,
pub children: KChildren, pub children: KChildren,
pub widget_name: WidgetName, pub widget_name: WidgetName,
} }
...@@ -28,40 +28,41 @@ impl Default for TabBundle { ...@@ -28,40 +28,41 @@ impl Default for TabBundle {
Self { Self {
tab: Default::default(), tab: Default::default(),
children: Default::default(), children: Default::default(),
styles: KStyle {
height: Units::Auto.into(),
top: Units::Pixels(0.0).into(),
..Default::default()
},
widget_name: Tab::default().get_name(), widget_name: Tab::default().get_name(),
} }
} }
} }
pub fn tab_update( pub fn tab_render(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
mut query: Query<(&KChildren, &mut Tab)>, query: Query<(&KChildren, &Tab)>,
mut tab_context_query: ParamSet<( tab_context_query: Query<&TabContext>,
Query<ChangeTrackers<TabContext>>,
Query<&mut TabContext, Changed<TabContext>>,
)>,
) -> bool { ) -> bool {
if !tab_context_query.p1().is_empty() { if let Ok((children, tab)) = query.get(entity) {
if let Ok((children, tab)) = query.get_mut(entity) { let context_entity = widget_context
let context_entity = widget_context .get_context_entity::<TabContext>(entity)
.get_context_entity::<TabContext>(entity) .unwrap();
.unwrap(); if let Ok(tab_context) = tab_context_query.get(context_entity) {
if let Ok(tab_context) = tab_context_query.p1().get(context_entity) { let parent_id = Some(entity);
let parent_id = Some(entity); let styles = KStyle {
let styles = KStyle { background_color: StyleProp::Value(Color::rgba(0.0781, 0.0898, 0.101, 1.0)),
background_color: StyleProp::Value(Color::rgba(0.0781, 0.0898, 0.101, 1.0)), padding: StyleProp::Value(Edge::all(Units::Pixels(15.0))),
padding: StyleProp::Value(Edge::all(Units::Pixels(5.0))), height: Units::Pixels(100.0).into(),
..Default::default() width: Units::Stretch(1.0).into(),
}; ..Default::default()
if tab_context.current_index == tab.index { };
rsx! { if tab_context.current_index == tab.index {
<BackgroundBundle styles={styles} children={children.clone()} /> rsx! {
} <BackgroundBundle styles={styles} children={children.clone()} />
} }
return true;
} }
} }
} }
false true
} }
use bevy::prelude::{Bundle, Changed, Color, Commands, Component, Entity, In, Query}; use bevy::prelude::{Bundle, Color, Commands, Component, Entity, In, Query};
use kayak_ui::prelude::{ use kayak_ui::prelude::{
rsx, rsx,
widgets::{KButtonBundle, TextProps, TextWidgetBundle}, widgets::{KButtonBundle, TextProps, TextWidgetBundle},
Event, EventDispatcherContext, EventType, KChildren, KStyle, OnEvent, StyleProp, Units, Widget, Event, EventDispatcherContext, EventType, KChildren, KStyle, OnEvent, StyleProp, Units, Widget,
WidgetContext, WidgetName, WidgetContext, WidgetName, WidgetProps, WidgetState,
}; };
use crate::tab_context::TabContext; use crate::tab_context::TabContext;
#[derive(Component, Default)] #[derive(Component, Default, PartialEq, Clone)]
pub struct TabButton { pub struct TabButton {
pub index: usize, pub index: usize,
pub title: String, pub title: String,
} }
impl Widget for TabButton {} impl Widget for TabButton {}
impl WidgetProps for TabButton {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct TabButtonBundle { pub struct TabButtonBundle {
...@@ -33,59 +34,57 @@ impl Default for TabButtonBundle { ...@@ -33,59 +34,57 @@ impl Default for TabButtonBundle {
} }
} }
pub fn tab_button_update( pub fn tab_button_render(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
query: Query<&TabButton>, query: Query<&TabButton>,
tab_context_query: Query<&mut TabContext, Changed<TabContext>>, tab_context_query: Query<&mut TabContext>,
) -> bool { ) -> bool {
if !tab_context_query.is_empty() { if let Ok(tab_button) = query.get(entity) {
if let Ok(tab_button) = query.get(entity) { let context_entity = widget_context
let context_entity = widget_context .get_context_entity::<TabContext>(entity)
.get_context_entity::<TabContext>(entity) .unwrap();
.unwrap(); if let Ok(tab_context) = tab_context_query.get(context_entity) {
if let Ok(tab_context) = tab_context_query.get(context_entity) { let background_color = if tab_context.current_index == tab_button.index {
let background_color = if tab_context.current_index == tab_button.index { Color::rgba(0.0781, 0.0898, 0.101, 1.0)
Color::rgba(0.0781, 0.0898, 0.101, 1.0) } else {
} else { Color::rgba(0.0781, 0.0898, 0.101, 0.75)
Color::rgba(0.0781, 0.0898, 0.101, 0.75) };
}; let parent_id = Some(entity);
let parent_id = Some(entity);
let button_index = tab_button.index; let button_index = tab_button.index;
let on_event = OnEvent::new( let on_event = OnEvent::new(
move |In((event_dispatcher_context, event, _entity)): In<( move |In((event_dispatcher_context, _, event, _entity)): In<(
EventDispatcherContext, EventDispatcherContext,
Event, WidgetState,
Entity, Event,
)>, Entity,
mut query: Query<&mut TabContext>| { )>,
match event.event_type { mut query: Query<&mut TabContext>| {
EventType::Click(..) => { match event.event_type {
if let Ok(mut tab_context) = query.get_mut(context_entity) { EventType::Click(..) => {
tab_context.current_index = button_index; if let Ok(mut tab_context) = query.get_mut(context_entity) {
} tab_context.current_index = button_index;
} }
_ => {}
} }
(event_dispatcher_context, event) _ => {}
}, }
); (event_dispatcher_context, event)
},
);
rsx! { rsx! {
<KButtonBundle <KButtonBundle
on_event={on_event} on_event={on_event}
styles={KStyle { styles={KStyle {
background_color: StyleProp::Value(background_color), background_color: StyleProp::Value(background_color),
height: StyleProp::Value(Units::Pixels(25.0)), height: StyleProp::Value(Units::Pixels(25.0)),
..Default::default() ..Default::default()
}}> }}>
<TextWidgetBundle text={TextProps { content: tab_button.title.clone(), ..Default::default() }} /> <TextWidgetBundle text={TextProps { content: tab_button.title.clone(), size: 16.0, line_height: Some(16.0), ..Default::default() }} />
</KButtonBundle> </KButtonBundle>
}
return true;
} }
} }
} }
false true
} }
use bevy::prelude::{Bundle, Changed, Commands, Component, Entity, In, Query}; use bevy::prelude::{Bundle, Commands, Component, Entity, In, Query};
use kayak_ui::prelude::{KChildren, Widget, WidgetContext, WidgetName}; use kayak_ui::prelude::*;
#[derive(Component, Default)] #[derive(Component, Default, PartialEq, Clone)]
pub struct TabContext { pub struct TabContext {
pub current_index: usize, pub current_index: usize,
} }
#[derive(Component, Default)] #[derive(Component, Default, PartialEq, Clone)]
pub struct TabContextProvider { pub struct TabContextProvider {
pub initial_index: usize, pub initial_index: usize,
} }
impl Widget for TabContextProvider {} impl Widget for TabContextProvider {}
impl WidgetProps for TabContextProvider {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct TabContextProviderBundle { pub struct TabContextProviderBundle {
pub tab_provider: TabContextProvider, pub tab_provider: TabContextProvider,
pub children: KChildren, pub children: KChildren,
pub styles: KStyle,
pub widget_name: WidgetName, pub widget_name: WidgetName,
} }
...@@ -25,30 +27,33 @@ impl Default for TabContextProviderBundle { ...@@ -25,30 +27,33 @@ impl Default for TabContextProviderBundle {
Self { Self {
tab_provider: Default::default(), tab_provider: Default::default(),
children: Default::default(), children: Default::default(),
styles: KStyle {
..Default::default()
},
widget_name: TabContextProvider::default().get_name(), widget_name: TabContextProvider::default().get_name(),
} }
} }
} }
pub fn tab_context_update( pub fn tab_context_render(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
query: Query< query: Query<(&KChildren, &TabContextProvider)>,
(&KChildren, &TabContextProvider),
(Changed<KChildren>, Changed<TabContextProvider>),
>,
) -> bool { ) -> bool {
if let Ok((children, tab_context_provider)) = query.get(entity) { if let Ok((children, tab_context_provider)) = query.get(entity) {
let context_entity = commands if widget_context
.spawn(TabContext { .get_context_entity::<TabContext>(entity)
current_index: tab_context_provider.initial_index, .is_none()
}) {
.id(); let context_entity = commands
widget_context.set_context_entity::<TabContext>(Some(entity), context_entity); .spawn(TabContext {
current_index: tab_context_provider.initial_index,
})
.id();
widget_context.set_context_entity::<TabContext>(Some(entity), context_entity);
}
children.process(&widget_context, Some(entity)); children.process(&widget_context, Some(entity));
return true;
} }
false true
} }
use bevy::{ use bevy::prelude::*;
prelude::{App as BevyApp, AssetServer, Commands, ImageSettings, Res, ResMut, Vec2},
DefaultPlugins,
};
use kayak_ui::prelude::{widgets::*, *}; use kayak_ui::prelude::{widgets::*, *};
mod tab; mod tab;
mod tab_button; mod tab_button;
mod tab_context; mod tab_context;
use tab::{tab_update, Tab, TabBundle}; use tab::{tab_render, Tab, TabBundle};
use tab_button::{tab_button_update, TabButton, TabButtonBundle}; use tab_button::{tab_button_render, TabButton, TabButtonBundle};
use tab_context::{tab_context_update, TabContextProvider, TabContextProviderBundle}; use tab_context::{tab_context_render, TabContextProvider, TabContextProviderBundle};
use crate::tab_context::TabContext;
fn startup( fn startup(
mut commands: Commands, mut commands: Commands,
...@@ -21,9 +20,24 @@ fn startup( ...@@ -21,9 +20,24 @@ fn startup(
commands.spawn(UICameraBundle::new()); commands.spawn(UICameraBundle::new());
let mut widget_context = Context::new(); let mut widget_context = Context::new();
widget_context.add_widget_system(Tab::default().get_name(), tab_update); widget_context.add_widget_data::<Tab, EmptyState>();
widget_context.add_widget_system(TabContextProvider::default().get_name(), tab_context_update); widget_context.add_widget_data::<TabContextProvider, EmptyState>();
widget_context.add_widget_system(TabButton::default().get_name(), tab_button_update); widget_context.add_widget_data::<TabButton, EmptyState>();
widget_context.add_widget_system(
Tab::default().get_name(),
widget_update_with_context::<Tab, EmptyState, TabContext>,
tab_render,
);
widget_context.add_widget_system(
TabContextProvider::default().get_name(),
widget_update_with_context::<TabContextProvider, EmptyState, TabContext>,
tab_context_render,
);
widget_context.add_widget_system(
TabButton::default().get_name(),
widget_update_with_context::<TabButton, EmptyState, TabContext>,
tab_button_render,
);
let parent_id = None; let parent_id = None;
rsx! { rsx! {
...@@ -41,20 +55,30 @@ fn startup( ...@@ -41,20 +55,30 @@ fn startup(
<ElementBundle <ElementBundle
styles={KStyle { styles={KStyle {
layout_type: StyleProp::Value(LayoutType::Row), layout_type: StyleProp::Value(LayoutType::Row),
height: StyleProp::Value(Units::Auto), height: StyleProp::Value(Units::Pixels(25.0)),
width: StyleProp::Value(Units::Stretch(1.0)), width: StyleProp::Value(Units::Stretch(1.0)),
..Default::default() ..Default::default()
}} }}
> >
<TabButtonBundle tab_button={TabButton { index: 0, title: "Tab 1".into() }} /> <TabButtonBundle
tab_button={TabButton { index: 0, title: "Tab 1".into(), }}
/>
<TabButtonBundle tab_button={TabButton { index: 1, title: "Tab 2".into() }} /> <TabButtonBundle tab_button={TabButton { index: 1, title: "Tab 2".into() }} />
</ElementBundle> </ElementBundle>
<TabBundle tab={Tab { index: 0 }}> <ElementBundle
<TextWidgetBundle text={TextProps { content: "Tab 1".into(), ..Default::default() }} /> styles={KStyle {
</TabBundle> height: StyleProp::Value(Units::Stretch(1.0)),
<TabBundle tab={Tab { index: 1 }}> width: StyleProp::Value(Units::Stretch(1.0)),
<TextWidgetBundle text={TextProps { content: "Tab 2".into(), ..Default::default() }} /> ..Default::default()
</TabBundle> }}
>
<TabBundle tab={Tab { index: 0 }}>
<TextWidgetBundle text={TextProps { content: "Tab 1 Content".into(), size: 14.0, line_height: Some(14.0), ..Default::default() }} />
</TabBundle>
<TabBundle tab={Tab { index: 1 }}>
<TextWidgetBundle text={TextProps { content: "Tab 2 Content".into(), size: 14.0, line_height: Some(14.0), ..Default::default() }} />
</TabBundle>
</ElementBundle>
</TabContextProviderBundle> </TabContextProviderBundle>
</WindowBundle> </WindowBundle>
</KayakAppBundle> </KayakAppBundle>
...@@ -64,7 +88,7 @@ fn startup( ...@@ -64,7 +88,7 @@ fn startup(
} }
fn main() { fn main() {
BevyApp::new() App::new()
.insert_resource(ImageSettings::default_nearest()) .insert_resource(ImageSettings::default_nearest())
.add_plugins(DefaultPlugins) .add_plugins(DefaultPlugins)
.add_plugin(ContextPlugin) .add_plugin(ContextPlugin)
......
...@@ -7,7 +7,7 @@ use bevy::{ ...@@ -7,7 +7,7 @@ use bevy::{
}; };
use kayak_ui::prelude::{widgets::*, KStyle, *}; use kayak_ui::prelude::{widgets::*, KStyle, *};
#[derive(Component, Default)] #[derive(Component, Default, Clone, PartialEq)]
pub struct MyWidgetProps { pub struct MyWidgetProps {
pub foo: u32, pub foo: u32,
} }
...@@ -33,6 +33,7 @@ fn my_widget_1_update( ...@@ -33,6 +33,7 @@ fn my_widget_1_update(
} }
impl Widget for MyWidgetProps {} impl Widget for MyWidgetProps {}
impl WidgetProps for MyWidgetProps {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct MyWidgetBundle { pub struct MyWidgetBundle {
...@@ -65,7 +66,12 @@ fn startup( ...@@ -65,7 +66,12 @@ fn startup(
let mut widget_context = Context::new(); let mut widget_context = Context::new();
let parent_id = None; let parent_id = None;
widget_context.add_widget_system(MyWidgetProps::default().get_name(), my_widget_1_update); widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
widget_context.add_widget_system(
MyWidgetProps::default().get_name(),
widget_update::<MyWidgetProps, EmptyState>,
my_widget_1_update,
);
rsx! { rsx! {
<KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle> <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle>
} }
......
use bevy::{ use bevy::{
prelude::{ prelude::{
Added, App as BevyApp, AssetServer, Bundle, Changed, Commands, Component, Entity, In, Or, App as BevyApp, AssetServer, Bundle, Commands, Component, Entity, In, Query, Res, ResMut,
ParamSet, Query, Res, ResMut, Vec2, With, Vec2,
}, },
DefaultPlugins, DefaultPlugins,
}; };
use kayak_ui::prelude::{widgets::*, *}; use kayak_ui::prelude::{widgets::*, *};
#[derive(Component, Default)] #[derive(Component, Default, Clone, PartialEq)]
struct TextBoxExample; struct TextBoxExample;
#[derive(Component, Default)] #[derive(Component, Default, Clone, PartialEq)]
struct TextBoxExampleState { struct TextBoxExampleState {
pub value1: String, pub value1: String,
pub value2: String, pub value2: String,
} }
impl Widget for TextBoxExample {} impl Widget for TextBoxExample {}
impl WidgetProps for TextBoxExample {}
#[derive(Bundle)] #[derive(Bundle)]
struct TextBoxExampleBundle { struct TextBoxExampleBundle {
...@@ -38,32 +39,18 @@ impl Default for TextBoxExampleBundle { ...@@ -38,32 +39,18 @@ impl Default for TextBoxExampleBundle {
fn update_text_box_example( fn update_text_box_example(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
props_query: Query< state_query: Query<&TextBoxExampleState>,
&TextBoxExample,
Or<(Changed<TextBoxExample>, Changed<KStyle>, With<Mounted>)>,
>,
mut state_query: ParamSet<(
Query<Entity, Or<(Added<TextBoxExampleState>, Changed<TextBoxExampleState>)>>,
Query<&TextBoxExampleState>,
)>,
) -> bool { ) -> bool {
if !props_query.is_empty() || !state_query.p0().is_empty() { let state_entity = widget_context.use_state::<TextBoxExampleState>(
let state_entity = widget_context.get_context_entity::<TextBoxExampleState>(entity); &mut commands,
if state_entity.is_none() { entity,
let state_entity = commands TextBoxExampleState {
.spawn(TextBoxExampleState { value1: "Hello World".into(),
value1: "Hello World".into(), value2: "Hello World2".into(),
value2: "Hello World2".into(), },
}) );
.id();
widget_context.set_context_entity::<TextBoxExampleState>(Some(entity), state_entity);
return false;
}
let state_entity = state_entity.unwrap();
let p1 = state_query.p1();
let textbox_state = p1.get(state_entity).unwrap();
if let Ok(textbox_state) = state_query.get(state_entity) {
let on_change = OnChange::new( let on_change = OnChange::new(
move |In((_widget_context, _, value)): In<(WidgetContext, Entity, String)>, move |In((_widget_context, _, value)): In<(WidgetContext, Entity, String)>,
mut state_query: Query<&mut TextBoxExampleState>| { mut state_query: Query<&mut TextBoxExampleState>| {
...@@ -99,11 +86,8 @@ fn update_text_box_example( ...@@ -99,11 +86,8 @@ fn update_text_box_example(
/> />
</ElementBundle> </ElementBundle>
} }
return true;
} }
true
false
} }
fn startup( fn startup(
...@@ -116,8 +100,11 @@ fn startup( ...@@ -116,8 +100,11 @@ fn startup(
commands.spawn(UICameraBundle::new()); commands.spawn(UICameraBundle::new());
let mut widget_context = Context::new(); let mut widget_context = Context::new();
widget_context.add_widget_data::<TextBoxExample, TextBoxExampleState>();
widget_context.add_widget_system( widget_context.add_widget_system(
TextBoxExample::default().get_name(), TextBoxExample::default().get_name(),
widget_update::<TextBoxExample, TextBoxExampleState>,
update_text_box_example, update_text_box_example,
); );
let parent_id = None; let parent_id = None;
......
...@@ -3,12 +3,13 @@ use kayak_ui::prelude::{widgets::*, *}; ...@@ -3,12 +3,13 @@ use kayak_ui::prelude::{widgets::*, *};
use crate::TodoList; use crate::TodoList;
#[derive(Component, Default)] #[derive(Component, Default, Clone, PartialEq)]
pub struct TodoInputProps { pub struct TodoInputProps {
has_focus: bool, has_focus: bool,
} }
impl Widget for TodoInputProps {} impl Widget for TodoInputProps {}
impl WidgetProps for TodoInputProps {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct TodoInputBundle { pub struct TodoInputBundle {
...@@ -36,7 +37,7 @@ impl Default for TodoInputBundle { ...@@ -36,7 +37,7 @@ impl Default for TodoInputBundle {
} }
} }
pub fn update_todo_input( pub fn render_todo_input(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
mut todo_list: ResMut<TodoList>, mut todo_list: ResMut<TodoList>,
...@@ -62,8 +63,9 @@ pub fn update_todo_input( ...@@ -62,8 +63,9 @@ pub fn update_todo_input(
} }
let handle_click = OnEvent::new( let handle_click = OnEvent::new(
move |In((event_dispatcher_context, event, _)): In<( move |In((event_dispatcher_context, _, event, _)): In<(
EventDispatcherContext, EventDispatcherContext,
WidgetState,
Event, Event,
Entity, Entity,
)>, )>,
...@@ -81,8 +83,9 @@ pub fn update_todo_input( ...@@ -81,8 +83,9 @@ pub fn update_todo_input(
); );
let handle_focus = OnEvent::new( let handle_focus = OnEvent::new(
move |In((event_dispatcher_context, event, _)): In<( move |In((event_dispatcher_context, _, event, _)): In<(
EventDispatcherContext, EventDispatcherContext,
WidgetState,
Event, Event,
Entity, Entity,
)>, )>,
......
...@@ -3,10 +3,11 @@ use kayak_ui::prelude::{widgets::*, *}; ...@@ -3,10 +3,11 @@ use kayak_ui::prelude::{widgets::*, *};
use crate::TodoList; use crate::TodoList;
#[derive(Component, Default)] #[derive(Component, Default, Clone, PartialEq)]
pub struct TodoItemsProps; pub struct TodoItemsProps;
impl Widget for TodoItemsProps {} impl Widget for TodoItemsProps {}
impl WidgetProps for TodoItemsProps {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct TodoItemsBundle { pub struct TodoItemsBundle {
...@@ -21,7 +22,7 @@ impl Default for TodoItemsBundle { ...@@ -21,7 +22,7 @@ impl Default for TodoItemsBundle {
widget: TodoItemsProps::default(), widget: TodoItemsProps::default(),
styles: KStyle { styles: KStyle {
render_command: StyleProp::Value(RenderCommand::Layout), render_command: StyleProp::Value(RenderCommand::Layout),
// height: StyleProp::Value(Units::Stretch(1.0)), height: StyleProp::Value(Units::Auto),
width: StyleProp::Value(Units::Stretch(1.0)), width: StyleProp::Value(Units::Stretch(1.0)),
..KStyle::default() ..KStyle::default()
}, },
...@@ -30,7 +31,7 @@ impl Default for TodoItemsBundle { ...@@ -30,7 +31,7 @@ impl Default for TodoItemsBundle {
} }
} }
pub fn update_todo_items( pub fn render_todo_items(
In((widget_context, entity)): In<(WidgetContext, Entity)>, In((widget_context, entity)): In<(WidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
todo_list: Res<TodoList>, todo_list: Res<TodoList>,
...@@ -39,11 +40,17 @@ pub fn update_todo_items( ...@@ -39,11 +40,17 @@ pub fn update_todo_items(
if query.is_empty() || todo_list.is_changed() { if query.is_empty() || todo_list.is_changed() {
let parent_id = Some(entity); let parent_id = Some(entity);
rsx! { rsx! {
<ElementBundle> <ElementBundle
styles={KStyle {
height: Units::Auto.into(),
..Default::default()
}}
>
{todo_list.items.iter().enumerate().for_each(|(index, content)| { {todo_list.items.iter().enumerate().for_each(|(index, content)| {
let handle_click = OnEvent::new( let handle_click = OnEvent::new(
move |In((event_dispatcher_context, event, _)): In<( move |In((event_dispatcher_context, _, event, _)): In<(
EventDispatcherContext, EventDispatcherContext,
WidgetState,
Event, Event,
Entity, Entity,
)>, )>,
......
...@@ -29,6 +29,18 @@ impl TodoList { ...@@ -29,6 +29,18 @@ impl TodoList {
} }
} }
// Our own version of widget_update that handles resource change events.
pub fn widget_update_with_resource<
Props: WidgetProps + PartialEq + Component + Clone,
State: PartialEq + Component + Clone,
>(
In((widget_context, entity, previous_entity)): In<(WidgetContext, Entity, Entity)>,
todo_list: Res<TodoList>,
widget_param: WidgetParam<Props, State>,
) -> bool {
widget_param.has_changed(&widget_context, entity, previous_entity) || todo_list.is_changed()
}
fn startup( fn startup(
mut commands: Commands, mut commands: Commands,
mut font_mapping: ResMut<FontMapping>, mut font_mapping: ResMut<FontMapping>,
...@@ -39,8 +51,19 @@ fn startup( ...@@ -39,8 +51,19 @@ fn startup(
commands.spawn(UICameraBundle::new()); commands.spawn(UICameraBundle::new());
let mut widget_context = Context::new(); let mut widget_context = Context::new();
widget_context.add_widget_system(TodoItemsProps::default().get_name(), update_todo_items); widget_context.add_widget_data::<TodoItemsProps, EmptyState>();
widget_context.add_widget_system(TodoInputProps::default().get_name(), update_todo_input); widget_context.add_widget_data::<TodoInputProps, EmptyState>();
widget_context.add_widget_system(
TodoItemsProps::default().get_name(),
widget_update_with_resource::<TodoItemsProps, EmptyState>,
render_todo_items,
);
widget_context.add_widget_system(
TodoInputProps::default().get_name(),
widget_update_with_resource::<TodoInputProps, EmptyState>,
render_todo_input,
);
let parent_id = None; let parent_id = None;
rsx! { rsx! {
<KayakAppBundle> <KayakAppBundle>
......
...@@ -7,7 +7,7 @@ use bevy::{ ...@@ -7,7 +7,7 @@ use bevy::{
}; };
use kayak_ui::prelude::{widgets::*, KStyle, *}; use kayak_ui::prelude::{widgets::*, KStyle, *};
#[derive(Component, Default)] #[derive(Component, Default, PartialEq, Clone)]
pub struct MyWidgetProps {} pub struct MyWidgetProps {}
fn my_widget_1_update( fn my_widget_1_update(
...@@ -42,6 +42,7 @@ fn my_widget_1_update( ...@@ -42,6 +42,7 @@ fn my_widget_1_update(
} }
impl Widget for MyWidgetProps {} impl Widget for MyWidgetProps {}
impl WidgetProps for MyWidgetProps {}
#[derive(Bundle)] #[derive(Bundle)]
pub struct MyWidgetBundle { pub struct MyWidgetBundle {
...@@ -71,7 +72,12 @@ fn startup( ...@@ -71,7 +72,12 @@ fn startup(
let mut widget_context = Context::new(); let mut widget_context = Context::new();
let parent_id = None; let parent_id = None;
widget_context.add_widget_system(MyWidgetProps::default().get_name(), my_widget_1_update); widget_context.add_widget_data::<MyWidgetProps, EmptyState>();
widget_context.add_widget_system(
MyWidgetProps::default().get_name(),
widget_update::<MyWidgetProps, EmptyState>,
my_widget_1_update,
);
rsx! { rsx! {
<KayakAppBundle><MyWidgetBundle /></KayakAppBundle> <KayakAppBundle><MyWidgetBundle /></KayakAppBundle>
} }
......
...@@ -199,8 +199,8 @@ impl Widget { ...@@ -199,8 +199,8 @@ impl Widget {
let props = quote! { let props = quote! {
let entity = widget_context.get_child_at(parent_id); let entity = widget_context.get_child_at(parent_id);
let #entity_id = if let Some(entity) = entity { let #entity_id = if let Some(entity) = entity {
use bevy::prelude::DespawnRecursiveExt; // use bevy::prelude::DespawnRecursiveExt;
commands.entity(entity).despawn_recursive(); // commands.entity(entity).despawn_recursive();
commands.get_or_spawn(entity).id() commands.get_or_spawn(entity).id()
} else { } else {
commands.spawn_empty().id() commands.spawn_empty().id()
......
...@@ -136,38 +136,24 @@ pub fn calculate_nodes( ...@@ -136,38 +136,24 @@ pub fn calculate_nodes(
commands.entity(entity).insert(node); commands.entity(entity).insert(node);
if !needs_layout { if !needs_layout {
commands.entity(entity).remove::<DirtyNode>(); commands.entity(entity).remove::<DirtyNode>();
log::trace!("{:?} needs layout!", entity.id());
} }
} }
// if has_new_nodes {
// build_nodes_tree(&mut context, &tree, &node_query);
// }
// dbg!("STARTING MORPHORM CALC!");
// dbg!("node_tree");
// context.node_tree.dump();
// if let Ok(tree) = context.tree.try_read() {
// dbg!("tree");
// dbg!(&tree);
// tree.dump();
// }
{ {
let context = context.as_mut(); let context = context.as_mut();
if let Ok(tree) = context.tree.try_read() { if let Ok(tree) = context.tree.try_read() {
// tree.dump();
let node_tree = &*tree; let node_tree = &*tree;
if let Ok(mut cache) = context.layout_cache.try_write() { if let Ok(mut cache) = context.layout_cache.try_write() {
let mut data_cache = DataCache { let mut data_cache = DataCache {
cache: &mut cache, cache: &mut cache,
query: &nodes_no_entity_query, query: &nodes_no_entity_query,
}; };
// dbg!(&node_tree);
morphorm::layout(&mut data_cache, node_tree, &nodes_no_entity_query); morphorm::layout(&mut data_cache, node_tree, &nodes_no_entity_query);
} }
} }
} }
// dbg!("FINISHED MORPHORM CALC!");
} }
} }
...@@ -200,6 +186,10 @@ fn create_primitive( ...@@ -200,6 +186,10 @@ fn create_primitive(
if let Some(parent_layout) = context.get_layout(&parent_id) { if let Some(parent_layout) = context.get_layout(&parent_id) {
properties.max_size = (parent_layout.width, parent_layout.height); properties.max_size = (parent_layout.width, parent_layout.height);
if properties.max_size.0 == 0.0 || properties.max_size.1 == 0.0 {
needs_layout = true;
}
// --- Calculate Text Layout --- // // --- Calculate Text Layout --- //
*text_layout = font.measure(&content, *properties); *text_layout = font.measure(&content, *properties);
let measurement = text_layout.size(); let measurement = text_layout.size();
......
...@@ -3,7 +3,7 @@ use bevy::prelude::*; ...@@ -3,7 +3,7 @@ use bevy::prelude::*;
use crate::prelude::WidgetContext; use crate::prelude::WidgetContext;
/// Defers widgets being added to the widget tree. /// Defers widgets being added to the widget tree.
#[derive(Component, Debug, Default, Clone)] #[derive(Component, Debug, Default, Clone, PartialEq)]
pub struct KChildren { pub struct KChildren {
inner: Vec<Entity>, inner: Vec<Entity>,
} }
......
use bevy::{ecs::system::CommandQueue, prelude::*};
use crate::widget_state::WidgetState;
#[derive(Component, Default)]
pub struct PreviousWidget;
#[derive(Default)]
pub(crate) struct EntityCloneSystems(
pub Vec<(
fn(&mut World, Entity, Entity),
fn(&mut World, Entity, Entity, &WidgetState),
)>,
);
pub(crate) fn clone_system<T: Clone + Component>(
world: &mut World,
target: Entity,
reference: Entity,
) {
if let Some(v) = world.entity(reference).get::<T>() {
let v = v.clone();
world.entity_mut(target).insert(v);
}
}
pub(crate) fn clone_state<State: Component + PartialEq + Clone>(
world: &mut World,
target: Entity,
reference: Entity,
widget_state: &WidgetState,
) {
if let Some(reference_state_entity) = widget_state.get(reference) {
if let Some(v) = world.entity(reference_state_entity).get::<State>() {
if let Some(target_state_entity) = widget_state.get(target) {
let v = v.clone();
world.entity_mut(target_state_entity).insert(v);
} else {
let mut command_queue = CommandQueue::default();
let mut commands = Commands::new(&mut command_queue, world);
let state_entity = widget_state.add::<State>(&mut commands, target, v.clone());
commands.entity(state_entity).insert(PreviousWidget);
command_queue.apply(world);
}
}
}
}
...@@ -9,6 +9,8 @@ use morphorm::Hierarchy; ...@@ -9,6 +9,8 @@ use morphorm::Hierarchy;
use crate::{ use crate::{
calculate_nodes::calculate_nodes, calculate_nodes::calculate_nodes,
children::KChildren,
clone_component::{clone_state, clone_system, EntityCloneSystems, PreviousWidget},
context_entities::ContextEntities, context_entities::ContextEntities,
event_dispatcher::EventDispatcher, event_dispatcher::EventDispatcher,
focus_tree::FocusTree, focus_tree::FocusTree,
...@@ -17,7 +19,10 @@ use crate::{ ...@@ -17,7 +19,10 @@ use crate::{
node::{DirtyNode, WrappedIndex}, node::{DirtyNode, WrappedIndex},
prelude::WidgetContext, prelude::WidgetContext,
render_primitive::RenderPrimitive, render_primitive::RenderPrimitive,
styles::KStyle,
tree::{Change, Tree}, tree::{Change, Tree},
widget::WidgetProps,
widget_state::WidgetState,
Focusable, WindowSize, Focusable, WindowSize,
}; };
...@@ -27,15 +32,26 @@ pub struct Mounted; ...@@ -27,15 +32,26 @@ pub struct Mounted;
const UPDATE_DEPTH: u32 = 0; const UPDATE_DEPTH: u32 = 0;
type WidgetSystems = HashMap<
String,
(
Box<dyn System<In = (WidgetContext, Entity, Entity), Out = bool>>,
Box<dyn System<In = (WidgetContext, Entity), Out = bool>>,
),
>;
#[derive(Resource)] #[derive(Resource)]
pub struct Context { pub struct Context {
pub tree: Arc<RwLock<Tree>>, pub tree: Arc<RwLock<Tree>>,
pub(crate) layout_cache: Arc<RwLock<LayoutCache>>, pub(crate) layout_cache: Arc<RwLock<LayoutCache>>,
pub(crate) focus_tree: Arc<RwLock<FocusTree>>, pub(crate) focus_tree: Arc<RwLock<FocusTree>>,
systems: HashMap<String, Box<dyn System<In = (WidgetContext, Entity), Out = bool>>>, systems: WidgetSystems,
pub(crate) current_z: f32, pub(crate) current_z: f32,
pub(crate) context_entities: ContextEntities, pub(crate) context_entities: ContextEntities,
pub(crate) current_cursor: CursorIcon, pub(crate) current_cursor: CursorIcon,
pub(crate) clone_systems: Arc<RwLock<EntityCloneSystems>>,
pub(crate) cloned_widget_entities: Arc<RwLock<HashMap<Entity, Entity>>>,
pub(crate) widget_state: WidgetState,
} }
impl Context { impl Context {
...@@ -48,6 +64,9 @@ impl Context { ...@@ -48,6 +64,9 @@ impl Context {
current_z: 0.0, current_z: 0.0,
context_entities: ContextEntities::new(), context_entities: ContextEntities::new(),
current_cursor: CursorIcon::Default, current_cursor: CursorIcon::Default,
clone_systems: Default::default(),
cloned_widget_entities: Default::default(),
widget_state: Default::default(),
} }
} }
...@@ -59,13 +78,29 @@ impl Context { ...@@ -59,13 +78,29 @@ impl Context {
} }
} }
pub fn add_widget_system<Params>( pub fn add_widget_system<Params, Params2>(
&mut self, &mut self,
type_name: impl Into<String>, type_name: impl Into<String>,
system: impl IntoSystem<(WidgetContext, Entity), bool, Params>, update: impl IntoSystem<(WidgetContext, Entity, Entity), bool, Params>,
render: impl IntoSystem<(WidgetContext, Entity), bool, Params2>,
) { ) {
let system = IntoSystem::into_system(system); let update_system = Box::new(IntoSystem::into_system(update));
self.systems.insert(type_name.into(), Box::new(system)); let render_system = Box::new(IntoSystem::into_system(render));
self.systems
.insert(type_name.into(), (update_system, render_system));
}
pub fn add_widget_data<
Props: WidgetProps + Component + Clone + PartialEq,
State: Component + Clone + PartialEq,
>(
&mut self,
) {
if let Ok(mut clone_systems) = self.clone_systems.try_write() {
clone_systems
.0
.push((clone_system::<Props>, clone_state::<State>));
}
} }
pub fn add_widget(&mut self, parent: Option<Entity>, entity: Entity) { pub fn add_widget(&mut self, parent: Option<Entity>, entity: Entity) {
...@@ -105,6 +140,7 @@ impl Context { ...@@ -105,6 +140,7 @@ impl Context {
pub fn build_render_primitives( pub fn build_render_primitives(
&self, &self,
nodes: &Query<&crate::node::Node>, nodes: &Query<&crate::node::Node>,
widget_names: &Query<&WidgetName>,
) -> Vec<RenderPrimitive> { ) -> Vec<RenderPrimitive> {
let node_tree = self.tree.try_read(); let node_tree = self.tree.try_read();
if node_tree.is_err() { if node_tree.is_err() {
...@@ -123,6 +159,7 @@ impl Context { ...@@ -123,6 +159,7 @@ impl Context {
&*node_tree, &*node_tree,
&self.layout_cache, &self.layout_cache,
nodes, nodes,
widget_names,
node_tree.root_node.unwrap(), node_tree.root_node.unwrap(),
0.0, 0.0,
RenderPrimitive::Empty, RenderPrimitive::Empty,
...@@ -134,6 +171,7 @@ fn recurse_node_tree_to_build_primitives( ...@@ -134,6 +171,7 @@ fn recurse_node_tree_to_build_primitives(
node_tree: &Tree, node_tree: &Tree,
layout_cache: &Arc<RwLock<LayoutCache>>, layout_cache: &Arc<RwLock<LayoutCache>>,
nodes: &Query<&crate::node::Node>, nodes: &Query<&crate::node::Node>,
widget_names: &Query<&WidgetName>,
current_node: WrappedIndex, current_node: WrappedIndex,
mut main_z_index: f32, mut main_z_index: f32,
mut prev_clip: RenderPrimitive, mut prev_clip: RenderPrimitive,
...@@ -151,6 +189,15 @@ fn recurse_node_tree_to_build_primitives( ...@@ -151,6 +189,15 @@ fn recurse_node_tree_to_build_primitives(
}; };
layout.z_index = new_z_index; layout.z_index = new_z_index;
render_primitive.set_layout(layout); render_primitive.set_layout(layout);
if matches!(render_primitive, RenderPrimitive::Empty) {
log::trace!(
"No render primitive for node: {}-{}",
widget_names.get(current_node.0).unwrap().0,
current_node.0.id(),
);
}
render_primitives.push(render_primitive.clone()); render_primitives.push(render_primitive.clone());
let new_prev_clip = if matches!(render_primitive, RenderPrimitive::Clip { .. }) { let new_prev_clip = if matches!(render_primitive, RenderPrimitive::Clip { .. }) {
...@@ -167,6 +214,7 @@ fn recurse_node_tree_to_build_primitives( ...@@ -167,6 +214,7 @@ fn recurse_node_tree_to_build_primitives(
node_tree, node_tree,
layout_cache, layout_cache,
nodes, nodes,
widget_names,
*child, *child,
main_z_index, main_z_index,
new_prev_clip.clone(), new_prev_clip.clone(),
...@@ -185,13 +233,40 @@ fn recurse_node_tree_to_build_primitives( ...@@ -185,13 +233,40 @@ fn recurse_node_tree_to_build_primitives(
render_primitives.push(prev_clip.clone()); render_primitives.push(prev_clip.clone());
} }
} }
} else {
log::trace!(
"No children for node: {}-{}",
widget_names.get(current_node.0).unwrap().0,
current_node.0.id()
);
} }
} else { } else {
println!("No layout for: {:?}", current_node.0.id()); log::warn!(
"No layout for node: {}-{}",
widget_names.get(current_node.0).unwrap().0,
current_node.0.id()
);
} }
} }
} else { } else {
println!("No node for: {:?}", current_node.0.id()); log::error!(
"No render node: {}-{} > {}-{}",
node_tree
.get_parent(current_node)
.and_then(|v| Some(v.0.id() as i32))
.unwrap_or(-1),
widget_names
.get(
node_tree
.get_parent(current_node)
.and_then(|v| Some(v.0))
.unwrap_or(Entity::from_raw(0))
)
.and_then(|v| Ok(v.0.clone()))
.unwrap_or("None".into()),
widget_names.get(current_node.0).unwrap().0,
current_node.0.id()
);
} }
render_primitives render_primitives
...@@ -229,6 +304,9 @@ fn update_widgets_sys(world: &mut World) { ...@@ -229,6 +304,9 @@ fn update_widgets_sys(world: &mut World) {
tree_iterator, tree_iterator,
&context.context_entities, &context.context_entities,
&context.focus_tree, &context.focus_tree,
&context.clone_systems,
&context.cloned_widget_entities,
&context.widget_state,
&mut new_ticks, &mut new_ticks,
); );
...@@ -245,9 +323,11 @@ fn update_widgets_sys(world: &mut World) { ...@@ -245,9 +323,11 @@ fn update_widgets_sys(world: &mut World) {
for (key, system) in context.systems.iter_mut() { for (key, system) in context.systems.iter_mut() {
if let Some(new_tick) = new_ticks.get(key) { if let Some(new_tick) = new_ticks.get(key) {
system.set_last_change_tick(*new_tick); system.0.set_last_change_tick(*new_tick);
system.1.set_last_change_tick(*new_tick);
} else { } else {
system.set_last_change_tick(tick); system.0.set_last_change_tick(tick);
system.1.set_last_change_tick(tick);
} }
// system.apply_buffers(world); // system.apply_buffers(world);
} }
...@@ -263,10 +343,13 @@ fn update_widgets( ...@@ -263,10 +343,13 @@ fn update_widgets(
world: &mut World, world: &mut World,
tree: &Arc<RwLock<Tree>>, tree: &Arc<RwLock<Tree>>,
layout_cache: &Arc<RwLock<LayoutCache>>, layout_cache: &Arc<RwLock<LayoutCache>>,
systems: &mut HashMap<String, Box<dyn System<In = (WidgetContext, Entity), Out = bool>>>, systems: &mut WidgetSystems,
widgets: Vec<WrappedIndex>, widgets: Vec<WrappedIndex>,
context_entities: &ContextEntities, context_entities: &ContextEntities,
focus_tree: &Arc<RwLock<FocusTree>>, focus_tree: &Arc<RwLock<FocusTree>>,
clone_systems: &Arc<RwLock<EntityCloneSystems>>,
cloned_widget_entities: &Arc<RwLock<HashMap<Entity, Entity>>>,
widget_state: &WidgetState,
new_ticks: &mut HashMap<String, u32>, new_ticks: &mut HashMap<String, u32>,
) { ) {
for entity in widgets.iter() { for entity in widgets.iter() {
...@@ -276,6 +359,7 @@ fn update_widgets( ...@@ -276,6 +359,7 @@ fn update_widgets(
tree.clone(), tree.clone(),
context_entities.clone(), context_entities.clone(),
layout_cache.clone(), layout_cache.clone(),
widget_state.clone(),
); );
widget_context.copy_from_point(&tree, *entity); widget_context.copy_from_point(&tree, *entity);
let children_before = widget_context.get_children(entity.0); let children_before = widget_context.get_children(entity.0);
...@@ -287,6 +371,9 @@ fn update_widgets( ...@@ -287,6 +371,9 @@ fn update_widgets(
widget_type.0.clone(), widget_type.0.clone(),
widget_context, widget_context,
children_before, children_before,
clone_systems,
cloned_widget_entities,
widget_state,
new_ticks, new_ticks,
); );
...@@ -321,6 +408,9 @@ fn update_widgets( ...@@ -321,6 +408,9 @@ fn update_widgets(
children, children,
context_entities, context_entities,
focus_tree, focus_tree,
clone_systems,
cloned_widget_entities,
widget_state,
new_ticks, new_ticks,
); );
// } // }
...@@ -340,26 +430,85 @@ fn update_widgets( ...@@ -340,26 +430,85 @@ fn update_widgets(
} }
fn update_widget( fn update_widget(
systems: &mut HashMap<String, Box<dyn System<In = (WidgetContext, Entity), Out = bool>>>, systems: &mut WidgetSystems,
tree: &Arc<RwLock<Tree>>, tree: &Arc<RwLock<Tree>>,
world: &mut World, world: &mut World,
entity: WrappedIndex, entity: WrappedIndex,
widget_type: String, widget_type: String,
widget_context: WidgetContext, widget_context: WidgetContext,
previous_children: Vec<Entity>, previous_children: Vec<Entity>,
clone_systems: &Arc<RwLock<EntityCloneSystems>>,
cloned_widget_entities: &Arc<RwLock<HashMap<Entity, Entity>>>,
widget_state: &WidgetState,
new_ticks: &mut HashMap<String, u32>, new_ticks: &mut HashMap<String, u32>,
) -> (Tree, bool) { ) -> (Tree, bool) {
// Check if we should update this widget
let should_rerender = {
let old_props_entity =
if let Ok(mut cloned_widget_entities) = cloned_widget_entities.try_write() {
if let Some(entity) = cloned_widget_entities.get(&entity.0) {
*entity
} else {
let target = world.spawn_empty().insert(PreviousWidget).id();
cloned_widget_entities.insert(entity.0, target);
target
}
} else {
panic!("Couldn't get write lock!")
};
let widget_update_system = &mut systems.get_mut(&widget_type).unwrap().0;
let old_tick = widget_update_system.get_last_change_tick();
let should_rerender =
widget_update_system.run((widget_context.clone(), 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);
if should_rerender {
if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() {
let target_entity = cloned_widget_entities.get(&entity.0).unwrap();
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() {
world.entity_mut(*target_entity).insert(styles);
}
if let Some(children) = world.entity(entity.0).get::<KChildren>().cloned() {
world.entity_mut(*target_entity).insert(children);
}
}
}
}
}
should_rerender
};
if !should_rerender {
return (widget_context.take(), false);
}
// if let Ok(tree) = tree.try_read() {
// if tree.root_node.unwrap() != entity {
// }
let should_update_children; let should_update_children;
log::trace!("Re-rendering: {:?} {:?}", &widget_type, entity.0.id());
{ {
// Remove children from previous render. // Remove children from previous render.
widget_context.remove_children(previous_children); widget_context.remove_children(previous_children);
let widget_system = systems.get_mut(&widget_type).unwrap(); let widget_render_system = &mut systems.get_mut(&widget_type).unwrap().1;
let old_tick = widget_system.get_last_change_tick(); let old_tick = widget_render_system.get_last_change_tick();
should_update_children = widget_system.run((widget_context.clone(), entity.0), world); should_update_children =
let new_tick = widget_system.get_last_change_tick(); widget_render_system.run((widget_context.clone(), entity.0), world);
let new_tick = widget_render_system.get_last_change_tick();
new_ticks.insert(widget_type.clone(), new_tick); new_ticks.insert(widget_type.clone(), new_tick);
widget_system.set_last_change_tick(old_tick); widget_render_system.set_last_change_tick(old_tick);
widget_system.apply_buffers(world); widget_render_system.apply_buffers(world);
} }
let widget_context = widget_context.take(); let widget_context = widget_context.take();
let mut command_queue = CommandQueue::default(); let mut command_queue = CommandQueue::default();
...@@ -367,22 +516,34 @@ fn update_widget( ...@@ -367,22 +516,34 @@ fn update_widget(
commands.entity(entity.0).remove::<Mounted>(); commands.entity(entity.0).remove::<Mounted>();
// Mark node as needing a recalculation of rendering/layout.
if should_update_children {
commands.entity(entity.0).insert(DirtyNode);
}
let diff = if let Ok(tree) = tree.read() { let diff = if let Ok(tree) = tree.read() {
tree.diff_children(&widget_context, entity, UPDATE_DEPTH) tree.diff_children(&widget_context, entity, UPDATE_DEPTH)
} else { } else {
panic!("Failed to acquire read lock."); panic!("Failed to acquire read lock.");
}; };
log::trace!("Diff: {:?}", &diff);
// Always mark widget dirty if it's re-rendered.
// Mark node as needing a recalculation of rendering/layout.
commands.entity(entity.0).insert(DirtyNode);
for child in widget_context.child_iter(entity) {
commands.entity(child.0).insert(DirtyNode);
}
if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() {
let target_entity = cloned_widget_entities.get(&entity.0).unwrap();
if let Some(styles) = world.entity(entity.0).get::<KStyle>().cloned() {
commands.entity(*target_entity).insert(styles);
}
if let Some(children) = world.entity(entity.0).get::<KChildren>().cloned() {
commands.entity(*target_entity).insert(children);
}
}
if should_update_children { if should_update_children {
for (_, changed_entity, _, changes) in diff.changes.iter() { for (_, changed_entity, _, changes) in diff.changes.iter() {
if changes.iter().any(|change| *change != Change::Deleted) {
commands.entity(changed_entity.0).insert(DirtyNode);
}
if changes.iter().any(|change| *change == Change::Deleted) { if changes.iter().any(|change| *change == Change::Deleted) {
// commands.entity(changed_entity.0).despawn(); // commands.entity(changed_entity.0).despawn();
commands.entity(changed_entity.0).remove::<DirtyNode>(); commands.entity(changed_entity.0).remove::<DirtyNode>();
...@@ -394,13 +555,27 @@ fn update_widget( ...@@ -394,13 +555,27 @@ fn update_widget(
} }
command_queue.apply(world); command_queue.apply(world);
if should_update_children {
for (_, child_entity, _, changes) in diff.changes.iter() {
// Clone to entity.
if changes.iter().any(|change| *change == Change::Deleted) {
if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() {
if let Some(entity) = cloned_widget_entities.get(&child_entity.0) {
world.despawn(*entity);
}
}
}
}
}
(widget_context, should_update_children) (widget_context, should_update_children)
} }
fn init_systems(world: &mut World) { fn init_systems(world: &mut World) {
let mut context = world.remove_resource::<Context>().unwrap(); let mut context = world.remove_resource::<Context>().unwrap();
for system in context.systems.values_mut() { for system in context.systems.values_mut() {
system.initialize(world); system.0.initialize(world);
system.1.initialize(world);
} }
world.insert_resource(context); world.insert_resource(context);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment