diff --git a/assets/panel.png b/assets/panel.png new file mode 100644 index 0000000000000000000000000000000000000000..739934c54b3e2103ddc44c3283f03c9dc08ac4a5 Binary files /dev/null and b/assets/panel.png differ diff --git a/bevy_kayak_ui/src/bevy_context.rs b/bevy_kayak_ui/src/bevy_context.rs index 0c2c974e74e5116e913d90fda7e2ad7c65e681c2..6708d8e761dffcdf631338eef44594b5800483d2 100644 --- a/bevy_kayak_ui/src/bevy_context.rs +++ b/bevy_kayak_ui/src/bevy_context.rs @@ -6,11 +6,11 @@ use kayak_core::{ styles::{Style, StyleProp, Units}, }; -pub struct BevyContext { - pub kayak_context: Arc<RwLock<KayakContext>>, +pub struct BevyContext<'a> { + pub kayak_context: Arc<RwLock<KayakContext<'a>>>, } -impl BevyContext { +impl<'a> BevyContext<'a> { pub fn new<F: Fn(&mut Style, &mut KayakContext)>(width: f32, height: f32, f: F) -> Self { let mut app_styles = Style { render_command: StyleProp::Value(RenderCommand::Window), @@ -23,9 +23,6 @@ impl BevyContext { if let Ok(mut kayak_context) = kayak_context.write() { f(&mut app_styles, &mut kayak_context); - - kayak_context.render(); - kayak_context.widget_manager.dirty(true); } diff --git a/bevy_kayak_ui/src/lib.rs b/bevy_kayak_ui/src/lib.rs index fee1bbc002e4c64ee1a418d83d638763af7f2603..ced795308fcbea7d33ee85587415dca11704289c 100644 --- a/bevy_kayak_ui/src/lib.rs +++ b/bevy_kayak_ui/src/lib.rs @@ -1,7 +1,9 @@ use bevy::{ input::{mouse::MouseButtonInput, ElementState}, math::Vec2, - prelude::{EventReader, MouseButton, Plugin, Res, ResMut}, + prelude::{ + EventReader, IntoExclusiveSystem, IntoSystem, MouseButton, Mut, Plugin, Res, ResMut, World, + }, render2::color::Color, window::{CursorMoved, Windows}, }; @@ -12,7 +14,7 @@ mod render; pub use bevy_context::BevyContext; pub use camera::*; -use kayak_core::InputEvent; +use kayak_core::{context::GlobalState, InputEvent}; #[derive(Default)] pub struct BevyKayakUIPlugin; @@ -21,7 +23,7 @@ impl Plugin for BevyKayakUIPlugin { fn build(&self, app: &mut bevy::prelude::App) { app.add_plugin(render::BevyKayakUIRenderPlugin) .add_plugin(camera::KayakUICameraPlugin) - .add_system(update); + .add_system(update.exclusive_system()); } } @@ -29,40 +31,61 @@ pub(crate) fn to_bevy_color(color: &kayak_core::color::Color) -> Color { Color::rgba(color.r, color.g, color.b, color.a) } -pub fn update( - bevy_context: ResMut<BevyContext>, - mut cursor_moved_events: EventReader<CursorMoved>, - mut mouse_button_input_events: EventReader<MouseButtonInput>, - windows: Res<Windows>, -) { - let window_size = if let Some(window) = windows.get_primary() { - Vec2::new(window.width(), window.height()) - } else { - panic!("Couldn't find primary window!"); +pub struct WrappedWorld<'a> { + world: &'a mut World, +} + +impl<'a> GlobalState for WrappedWorld<'a> {} + +pub fn update(world: &mut World) { + let window_size = { + let windows = world.get_resource::<Windows>().unwrap(); + if let Some(window) = windows.get_primary() { + Vec2::new(window.width(), window.height()) + } else { + panic!("Couldn't find primary window!"); + } }; + let bevy_context = world.remove_resource::<BevyContext<'static>>().unwrap(); if let Ok(mut context) = bevy_context.kayak_context.write() { - context.render(); + let mut wrapped_world = WrappedWorld { world }; + context.render(&mut wrapped_world); + } + if let Ok(mut context) = bevy_context.kayak_context.write() { let mut input_events = Vec::new(); - for event in cursor_moved_events.iter() { - input_events.push(InputEvent::MouseMoved(( - event.position.x as f32, - window_size.y - event.position.y as f32, - ))); + + { + let mut cursor_moved_events = world + .get_resource_mut::<EventReader<CursorMoved>>() + .unwrap(); + for event in cursor_moved_events.iter() { + input_events.push(InputEvent::MouseMoved(( + event.position.x as f32, + window_size.y - event.position.y as f32, + ))); + } } - for event in mouse_button_input_events.iter() { - match event.button { - MouseButton::Left => { - if event.state == ElementState::Pressed { - input_events.push(InputEvent::MouseLeftClick); + { + let mut mouse_button_input_events = world + .get_resource_mut::<EventReader<MouseButtonInput>>() + .unwrap(); + for event in mouse_button_input_events.iter() { + match event.button { + MouseButton::Left => { + if event.state == ElementState::Pressed { + input_events.push(InputEvent::MouseLeftClick); + } } + _ => {} } - _ => {} } } context.process_events(input_events); } + + world.insert_resource(bevy_context); } diff --git a/bevy_kayak_ui/src/render/unified/font/extract.rs b/bevy_kayak_ui/src/render/unified/font/extract.rs index ce8844dde4d434a346ecd64e354e29836e7a6317..585f124a89a3fc9f9bb2299df7e95597e5980908 100644 --- a/bevy_kayak_ui/src/render/unified/font/extract.rs +++ b/bevy_kayak_ui/src/render/unified/font/extract.rs @@ -14,7 +14,7 @@ use super::{font::KayakFont, font_mapping::FontMapping}; pub fn extract_texts( mut commands: Commands, - context: Res<BevyContext>, + context: Res<BevyContext<'static>>, mut fonts: ResMut<Assets<KayakFont>>, font_mapping: Res<FontMapping>, ) { diff --git a/bevy_kayak_ui/src/render/unified/quad/extract.rs b/bevy_kayak_ui/src/render/unified/quad/extract.rs index 20a4c8ec13fefe9e1b58418ce777747b23dbe1ab..1290ae56286f9dbb7482a426ef7a6bf21aeddcf3 100644 --- a/bevy_kayak_ui/src/render/unified/quad/extract.rs +++ b/bevy_kayak_ui/src/render/unified/quad/extract.rs @@ -10,7 +10,7 @@ use crate::{ to_bevy_color, BevyContext, }; -pub fn extract_quads(mut commands: Commands, context: Res<BevyContext>) { +pub fn extract_quads(mut commands: Commands, context: Res<BevyContext<'static>>) { let render_commands = if let Ok(context) = context.kayak_context.read() { context.widget_manager.build_render_primitives() } else { diff --git a/kayak_components/src/image.rs b/kayak_components/src/image.rs new file mode 100644 index 0000000000000000000000000000000000000000..f67f2934983a6a89ff3914bb366331ec90a96587 --- /dev/null +++ b/kayak_components/src/image.rs @@ -0,0 +1,15 @@ +use kayak_core::{component, rsx, Render, Update}; + +#[component] +pub fn Image<Children: Render + Update + Clone>(handle: u16, children: Children) { + *styles = Some(Style { + render_command: StyleProp::Value(RenderCommand::Image { handle }), + ..styles.clone().unwrap_or_default() + }); + + rsx! { + <> + {children} + </> + } +} diff --git a/kayak_core/src/context.rs b/kayak_core/src/context.rs index b18aa0ef8c79a7e4d7ea06e36515673cc6e77441..bcd89da329aec8ecaf96a8c2d6f6af4f066e5e6d 100644 --- a/kayak_core/src/context.rs +++ b/kayak_core/src/context.rs @@ -1,17 +1,21 @@ use std::collections::HashMap; +use as_any::AsAny; use resources::Ref; use crate::{node::NodeIndex, widget_manager::WidgetManager, Event, EventType, Index, InputEvent}; -pub struct KayakContext { +pub trait GlobalState: Send + Sync {} + +pub struct KayakContext<'a> { component_states: HashMap<crate::Index, resources::Resources>, current_id: crate::Index, pub widget_manager: WidgetManager, last_mouse_position: (f32, f32), + global_state: Option<&'a mut dyn GlobalState>, } -impl std::fmt::Debug for KayakContext { +impl<'a> std::fmt::Debug for KayakContext<'a> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("KayakContext") .field("current_id", &self.current_id) @@ -19,13 +23,14 @@ impl std::fmt::Debug for KayakContext { } } -impl KayakContext { +impl<'a> KayakContext<'a> { pub fn new() -> Self { Self { component_states: HashMap::new(), current_id: crate::Index::default(), widget_manager: WidgetManager::new(), last_mouse_position: (0.0, 0.0), + global_state: None, } } @@ -78,7 +83,9 @@ impl KayakContext { } } - pub fn render(&mut self) { + pub fn render(&mut self, global_state: &'a mut dyn GlobalState) { + self.global_state = Some(global_state); + let dirty_nodes = self.widget_manager.dirty_nodes.clone(); for node_index in dirty_nodes { if self @@ -97,6 +104,8 @@ impl KayakContext { self.widget_manager.render(); self.widget_manager.calculate_layout(); + + self.global_state = None; } pub fn process_events(&mut self, input_events: Vec<InputEvent>) { diff --git a/kayak_core/src/lib.rs b/kayak_core/src/lib.rs index 47009cc660238e08faefea66b6ec6915f67e2674..268b555f0747cfb9eebce923067bba9fa3f08478 100644 --- a/kayak_core/src/lib.rs +++ b/kayak_core/src/lib.rs @@ -22,8 +22,15 @@ pub use input_event::*; pub use kayak_render_macros::{render, rsx, widget}; pub use widget::Widget; -pub type Children = - Option<Arc<dyn Fn(Option<crate::Index>, &mut crate::context::KayakContext) + Send + Sync>>; +pub type Children = Option< + Arc< + dyn for<'global_state> Fn( + Option<crate::Index>, + &mut crate::context::KayakContext, + ) + Send + + Sync, + >, +>; #[derive(Clone)] pub struct OnEvent( diff --git a/kayak_core/src/render_command.rs b/kayak_core/src/render_command.rs index 31f034efcb8c6cbb7cd804da0fa2984288b605e4..cb6839f829e54b80adcb4910028ed9ed6084ef12 100644 --- a/kayak_core/src/render_command.rs +++ b/kayak_core/src/render_command.rs @@ -9,6 +9,9 @@ pub enum RenderCommand { size: f32, font: u16, }, + Image { + handle: u16, + }, } impl Default for RenderCommand { diff --git a/kayak_core/src/render_primitive.rs b/kayak_core/src/render_primitive.rs index 3208f6c1f4fe58fe4f16149fb7e02d5fd279659d..433c21687c34026412005f73906c85a24e62d140 100644 --- a/kayak_core/src/render_primitive.rs +++ b/kayak_core/src/render_primitive.rs @@ -23,6 +23,9 @@ pub enum RenderPrimitive { content: String, font: u16, }, + Image { + handle: u16, + }, } impl RenderPrimitive { @@ -68,6 +71,7 @@ impl From<&Style> for RenderPrimitive { content, font, }, + RenderCommand::Image { handle } => Self::Image { handle }, } } }