diff --git a/book/src/chapter_1.md b/book/src/chapter_1.md index 419a831e8cc512470796b046c890b72cb608f99f..1f820f085398a6084c627b6091a804b909e21ff0 100644 --- a/book/src/chapter_1.md +++ b/book/src/chapter_1.md @@ -39,7 +39,8 @@ fn startup( /> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { @@ -98,7 +99,8 @@ fn startup( widget_context.add_widget(None, app_entity); // Add widget context as resource. - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { App::new() diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs index e57e0db694de47c4df736b186279026e4bb580c0..096ee9f9f901cbb67fa6ec05682308352a500a5c 100644 --- a/examples/bevy_scene.rs +++ b/examples/bevy_scene.rs @@ -38,7 +38,7 @@ struct WorldCamera; fn set_active_tile_target( mut tile: Query<&mut ActiveTile>, cursor: Res<Input<MouseButton>>, - event_context: Res<EventDispatcher>, + event_context: Query<&EventDispatcher, With<GameUI>>, camera_transform: Query<&GlobalTransform, With<WorldCamera>>, windows: Res<Windows>, ) { @@ -47,7 +47,7 @@ fn set_active_tile_target( return; } - if event_context.contains_cursor() { + if event_context.single().contains_cursor() { // This is the important bit: // If the cursor is over a part of the UI, then we should not allow clicks to pass through to the world return; @@ -79,14 +79,14 @@ fn move_active_tile(mut tile: Query<(&mut Transform, &ActiveTile)>) { /// A system that moves the ghost tile to the cursor's position fn move_ghost_tile( - event_context: Res<EventDispatcher>, + event_context: Query<&EventDispatcher, With<GameUI>>, mut tile: Query<&mut Transform, With<GhostTile>>, mut cursor_moved: EventReader<CursorMoved>, camera_transform: Query<&GlobalTransform, With<WorldCamera>>, windows: Res<Windows>, ) { for _ in cursor_moved.iter() { - if !event_context.contains_cursor() { + if !event_context.single().contains_cursor() { let world_pos = cursor_to_world(&windows, camera_transform.single()); let tile_pos = world_to_tile(world_pos); let mut ghost = tile.single_mut(); @@ -144,7 +144,6 @@ fn cursor_to_world(windows: &Windows, camera_transform: &GlobalTransform) -> Vec let size = Vec2::new(window.width(), window.height()); let mut pos = window.cursor_position().unwrap_or_default(); - pos.y = size.y - pos.y; pos -= size / 2.0; let point = camera_transform.compute_matrix() * pos.extend(0.0).extend(1.0); @@ -165,6 +164,9 @@ fn ghost_color(color: Color) -> Color { c } +#[derive(Component)] +pub struct GameUI; + fn startup( mut commands: Commands, mut font_mapping: ResMut<FontMapping>, @@ -172,9 +174,8 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let handle_change_color = OnEvent::new( move |In((event_dispatcher_context, _, event, _)): In<( @@ -254,7 +255,7 @@ fn startup( </KayakAppBundle> } - commands.insert_resource(widget_context); + commands.spawn((UICameraBundle::new(widget_context), GameUI)); } fn main() { diff --git a/examples/clipping.rs b/examples/clipping.rs index b58313779fd5860584b02df79743aaa7aac29da1..ee5a3032f5133edca1ad952381d1f22f4c866f88 100644 --- a/examples/clipping.rs +++ b/examples/clipping.rs @@ -8,11 +8,10 @@ fn startup( ) { font_mapping.set_default(asset_server.load("lato-light.kayak_font")); - commands.spawn(UICameraBundle::new()); - let image = asset_server.load("panel.png"); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; let nine_patch_styles = KStyle { @@ -56,7 +55,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. </NinePatchBundle> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/conditional_widget.rs b/examples/conditional_widget.rs index a6de4d662b0dcc7e9fa79b595a9c8c38a45e39f7..b668f17ce4655b6706002df9a4dcf201fb0b84fb 100644 --- a/examples/conditional_widget.rs +++ b/examples/conditional_widget.rs @@ -114,9 +114,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidget, MyWidgetState>(); widget_context.add_widget_system( @@ -129,7 +129,8 @@ fn startup( <MyWidgetBundle /> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/context.rs b/examples/context.rs index 8147520f443f647dcce9a9f6fd38ec5701bb37a3..aa3696f59c8a94ccbcd584620bfba509ce0e9688 100644 --- a/examples/context.rs +++ b/examples/context.rs @@ -330,9 +330,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<ThemeDemo, EmptyState>(); widget_context.add_widget_data::<ThemeButton, EmptyState>(); widget_context.add_widget_data::<ThemeSelector, EmptyState>(); @@ -376,7 +376,8 @@ fn startup( </WindowBundle> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/demo.rs b/examples/demo.rs index 2d63f7266a47d4e5c7a5c337df6791f6a5e3837b..0ba501e2ec1b438fcfab777ade91007f1ae7384e 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -22,6 +22,7 @@ impl Widget for MyWidget {} fn startup(mut commands: Commands) { let mut context = KayakRootContext::new(); + context.add_plugin(KayakWidgetsContextPlugin); context.add_widget_system( MyWidget::default().get_name(), widget_update::<MyWidget, EmptyState>, @@ -46,7 +47,7 @@ fn startup(mut commands: Commands) { }); context.add_widget(None, app_entity); - commands.insert_resource(context); + commands.spawn(UICameraBundle::new(context)); } // Note this example shows prop changing not state changing which is quite different. diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 87cc694a572034eac121139c0e44e9ac769a1cce..e57e6f3aa380cfb6c15bd0a26b8483334b3dd0b9 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -8,9 +8,8 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { <KayakAppBundle> @@ -23,7 +22,8 @@ fn startup( /> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs index 647e31db7f387e8a0b2710ef869f6e15222e8389..1b472f8eb962ed39ece9044244278655b1d0005b 100644 --- a/examples/hello_world_no_macro.rs +++ b/examples/hello_world_no_macro.rs @@ -6,8 +6,8 @@ fn startup( asset_server: Res<AssetServer>, ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let app_entity = widget_context.spawn_widget(&mut commands, None); // Create default app bundle @@ -19,7 +19,7 @@ fn startup( let mut children = KChildren::new(); // Create the text child - let text_entity = widget_context.spawn_widget(&mut commands, Some(app_entity)); + let text_entity = widget_context.spawn_widget(&mut commands, None); commands.entity(text_entity).insert(TextWidgetBundle { text: TextProps { content: "Hello World".into(), @@ -38,7 +38,8 @@ fn startup( widget_context.add_widget(None, app_entity); // Add widget context as resource. - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { App::new() diff --git a/examples/image.rs b/examples/image.rs index 03cac2b74d6477463abaceb386ec106d642d1e08..1b490ee054dc4fe355df9255c716da4f60ffb527 100644 --- a/examples/image.rs +++ b/examples/image.rs @@ -8,11 +8,10 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let image = asset_server.load("generic-rpg-vendor.png"); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { <KayakAppBundle> @@ -30,7 +29,8 @@ fn startup( /> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/main_menu.rs b/examples/main_menu.rs index a67d67bf27b210bc85056115b8f60092606cb979..a1ec90cd1e51d239f9b45a14e9249b9db4031f24 100644 --- a/examples/main_menu.rs +++ b/examples/main_menu.rs @@ -122,9 +122,8 @@ fn startup( ) { font_mapping.set_default(asset_server.load("lato-light.kayak_font")); - commands.spawn(UICameraBundle::new()); - let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<MenuButton, ButtonState>(); widget_context.add_widget_system( MenuButton::default().get_name(), @@ -214,7 +213,7 @@ fn startup( </KayakAppBundle> } - commands.insert_resource(widget_context); + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs index 2ddf562dbaf355cb75515031e0a41dc8a9e3dd7f..5ff1e0d7c10a73cb3123e6208d41a93dac369a1c 100644 --- a/examples/nine_patch.rs +++ b/examples/nine_patch.rs @@ -8,11 +8,10 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let image = asset_server.load("panel.png"); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; // The border prop splits up the image into 9 quadrants like so: @@ -52,7 +51,7 @@ fn startup( </KayakAppBundle> } - commands.insert_resource(widget_context); + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/quads.rs b/examples/quads.rs index 5e6523107cc2ecd3aff5dc1ada1fb9717cf7652b..e0afabedbe25817f52a5b68a6e396174380d72f9 100644 --- a/examples/quads.rs +++ b/examples/quads.rs @@ -90,9 +90,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_system( MyQuad::default().get_name(), widget_update::<MyQuad, EmptyState>, @@ -125,7 +125,7 @@ fn startup( </KayakAppBundle> } - commands.insert_resource(widget_context); + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/render_target.rs b/examples/render_target.rs index 763d5c69723a430fdd5d3bf07aa6fd5c79028d92..3df1195c9eee8d323b89fb00f5ce26e58d1452bb 100644 --- a/examples/render_target.rs +++ b/examples/render_target.rs @@ -53,19 +53,8 @@ fn startup( let image_handle = images.add(image); - commands.spawn(UICameraBundle { - camera: Camera { - priority: -1, - target: RenderTarget::Image(image_handle.clone()), - ..Camera::default() - }, - camera_ui: CameraUIKayak { - clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default, - }, - ..UICameraBundle::new() - }); - let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { <KayakAppBundle @@ -83,7 +72,18 @@ fn startup( /> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle { + camera: Camera { + priority: -1, + target: RenderTarget::Image(image_handle.clone()), + ..Camera::default() + }, + camera_ui: CameraUIKayak { + clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default, + }, + ..UICameraBundle::new(widget_context) + }); // Setup 3D scene // Light diff --git a/examples/scrolling.rs b/examples/scrolling.rs index baef12e0230ffbca9b0d17c29ff47b0bd0546deb..c4c23871bbcb5d7153edafebf6531b9250585c11 100644 --- a/examples/scrolling.rs +++ b/examples/scrolling.rs @@ -1,67 +1,68 @@ -use bevy::prelude::*; -use kayak_ui::prelude::{widgets::*, *}; - -fn startup( - mut commands: Commands, - mut font_mapping: ResMut<FontMapping>, - asset_server: Res<AssetServer>, -) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); - - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); - - let mut widget_context = KayakRootContext::new(); - let parent_id = None; - - let lorem_ipsum = r#" -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. Proin tempus ligula a mi molestie aliquam. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam venenatis consequat ultricies. Sed ac orci purus. Nullam velit nisl, dapibus vel mauris id, dignissim elementum sapien. Vestibulum faucibus sapien ut erat bibendum, id lobortis nisi luctus. Mauris feugiat at lectus at pretium. Pellentesque vitae finibus ante. Nulla non ex neque. Cras varius, lorem facilisis consequat blandit, lorem mauris mollis massa, eget consectetur magna sem vel enim. Nam aliquam risus pulvinar, volutpat leo eget, eleifend urna. Suspendisse in magna sed ligula vehicula volutpat non vitae augue. Phasellus aliquam viverra consequat. Nam rhoncus molestie purus, sed laoreet neque imperdiet eget. Sed egestas metus eget sodales congue. - - Sed vel ante placerat, posuere lacus sit amet, tempus enim. Cras ullamcorper ex vitae metus consequat, a blandit leo semper. Nunc lacinia porta massa, a tempus leo laoreet nec. Sed vel metus tincidunt, scelerisque ex sit amet, lacinia dui. In sollicitudin pulvinar odio vitae hendrerit. Maecenas mollis tempor egestas. Nulla facilisi. Praesent nisi turpis, accumsan eu lobortis vestibulum, ultrices id nibh. Suspendisse sed dui porta, mollis elit sed, ornare sem. Cras molestie est libero, quis faucibus leo semper at. - - Nulla vel nisl rutrum, fringilla elit non, mollis odio. Donec convallis arcu neque, eget venenatis sem mattis nec. Nulla facilisi. Phasellus risus elit, vehicula sit amet risus et, sodales ultrices est. Quisque vulputate felis orci, non tristique leo faucibus in. Duis quis velit urna. Sed rhoncus dolor vel commodo aliquet. In sed tempor quam. Nunc non tempus ipsum. Praesent mi lacus, vehicula eu dolor eu, condimentum venenatis diam. In tristique ligula a ligula dictum, eu dictum lacus consectetur. Proin elementum egestas pharetra. Nunc suscipit dui ac nisl maximus, id congue velit volutpat. Etiam condimentum, mauris ac sodales tristique, est augue accumsan elit, ut luctus est mi ut urna. Mauris commodo, tortor eget gravida lacinia, leo est imperdiet arcu, a ullamcorper dui sapien eget erat. - - Vivamus pulvinar dui et elit volutpat hendrerit. Praesent luctus dolor ut rutrum finibus. Fusce ut odio ultrices, laoreet est at, condimentum turpis. Morbi at ultricies nibh. Mauris tempus imperdiet porta. Proin sit amet tincidunt eros. Quisque rutrum lacus ac est vehicula dictum. Pellentesque nec augue mi. - - Vestibulum rutrum imperdiet nisl, et consequat massa porttitor vel. Ut velit justo, vehicula a nulla eu, auctor eleifend metus. Ut egestas malesuada metus, sit amet pretium nunc commodo ac. Pellentesque gravida, nisl in faucibus volutpat, libero turpis mattis orci, vitae tincidunt ligula ligula ut tortor. Maecenas vehicula lobortis odio in molestie. Curabitur dictum elit sed arcu dictum, ut semper nunc cursus. Donec semper felis non nisl tincidunt elementum. - "#.to_string(); - - rsx! { - <KayakAppBundle> - <WindowBundle - window={KWindow { - title: "Simple scrolling example".into(), - draggable: true, - initial_position: Vec2::new(10.0, 10.0), - size: Vec2::new(512.0, 512.0), - ..KWindow::default() - }} - > - <ScrollContextProviderBundle> - <ScrollBoxBundle> - <TextWidgetBundle - text={TextProps { - content: lorem_ipsum, - size: 14.0, - ..Default::default() - }} - /> - </ScrollBoxBundle> - </ScrollContextProviderBundle> - </WindowBundle> - </KayakAppBundle> - } - commands.insert_resource(widget_context); -} - -fn main() { - App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) - .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) - .add_plugin(KayakContextPlugin) - .add_plugin(KayakWidgets) - .add_startup_system(startup) - .run() -} +use bevy::prelude::*; +use kayak_ui::prelude::{widgets::*, *}; + +fn startup( + mut commands: Commands, + mut font_mapping: ResMut<FontMapping>, + asset_server: Res<AssetServer>, +) { + font_mapping.set_default(asset_server.load("roboto.kayak_font")); + + // Camera 2D forces a clear pass in bevy. + // We do this because our scene is not rendering anything else. + commands.spawn(Camera2dBundle::default()); + + let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); + let parent_id = None; + + let lorem_ipsum = r#" +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. Proin tempus ligula a mi molestie aliquam. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam venenatis consequat ultricies. Sed ac orci purus. Nullam velit nisl, dapibus vel mauris id, dignissim elementum sapien. Vestibulum faucibus sapien ut erat bibendum, id lobortis nisi luctus. Mauris feugiat at lectus at pretium. Pellentesque vitae finibus ante. Nulla non ex neque. Cras varius, lorem facilisis consequat blandit, lorem mauris mollis massa, eget consectetur magna sem vel enim. Nam aliquam risus pulvinar, volutpat leo eget, eleifend urna. Suspendisse in magna sed ligula vehicula volutpat non vitae augue. Phasellus aliquam viverra consequat. Nam rhoncus molestie purus, sed laoreet neque imperdiet eget. Sed egestas metus eget sodales congue. + + Sed vel ante placerat, posuere lacus sit amet, tempus enim. Cras ullamcorper ex vitae metus consequat, a blandit leo semper. Nunc lacinia porta massa, a tempus leo laoreet nec. Sed vel metus tincidunt, scelerisque ex sit amet, lacinia dui. In sollicitudin pulvinar odio vitae hendrerit. Maecenas mollis tempor egestas. Nulla facilisi. Praesent nisi turpis, accumsan eu lobortis vestibulum, ultrices id nibh. Suspendisse sed dui porta, mollis elit sed, ornare sem. Cras molestie est libero, quis faucibus leo semper at. + + Nulla vel nisl rutrum, fringilla elit non, mollis odio. Donec convallis arcu neque, eget venenatis sem mattis nec. Nulla facilisi. Phasellus risus elit, vehicula sit amet risus et, sodales ultrices est. Quisque vulputate felis orci, non tristique leo faucibus in. Duis quis velit urna. Sed rhoncus dolor vel commodo aliquet. In sed tempor quam. Nunc non tempus ipsum. Praesent mi lacus, vehicula eu dolor eu, condimentum venenatis diam. In tristique ligula a ligula dictum, eu dictum lacus consectetur. Proin elementum egestas pharetra. Nunc suscipit dui ac nisl maximus, id congue velit volutpat. Etiam condimentum, mauris ac sodales tristique, est augue accumsan elit, ut luctus est mi ut urna. Mauris commodo, tortor eget gravida lacinia, leo est imperdiet arcu, a ullamcorper dui sapien eget erat. + + Vivamus pulvinar dui et elit volutpat hendrerit. Praesent luctus dolor ut rutrum finibus. Fusce ut odio ultrices, laoreet est at, condimentum turpis. Morbi at ultricies nibh. Mauris tempus imperdiet porta. Proin sit amet tincidunt eros. Quisque rutrum lacus ac est vehicula dictum. Pellentesque nec augue mi. + + Vestibulum rutrum imperdiet nisl, et consequat massa porttitor vel. Ut velit justo, vehicula a nulla eu, auctor eleifend metus. Ut egestas malesuada metus, sit amet pretium nunc commodo ac. Pellentesque gravida, nisl in faucibus volutpat, libero turpis mattis orci, vitae tincidunt ligula ligula ut tortor. Maecenas vehicula lobortis odio in molestie. Curabitur dictum elit sed arcu dictum, ut semper nunc cursus. Donec semper felis non nisl tincidunt elementum. + "#.to_string(); + + rsx! { + <KayakAppBundle> + <WindowBundle + window={KWindow { + title: "Simple scrolling example".into(), + draggable: true, + initial_position: Vec2::new(10.0, 10.0), + size: Vec2::new(512.0, 512.0), + ..KWindow::default() + }} + > + <ScrollContextProviderBundle> + <ScrollBoxBundle> + <TextWidgetBundle + text={TextProps { + content: lorem_ipsum, + size: 14.0, + ..Default::default() + }} + /> + </ScrollBoxBundle> + </ScrollContextProviderBundle> + </WindowBundle> + </KayakAppBundle> + } + + commands.spawn(UICameraBundle::new(widget_context)); +} + +fn main() { + App::new() + .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) + .add_plugin(KayakContextPlugin) + .add_plugin(KayakWidgets) + .add_startup_system(startup) + .run() +} diff --git a/examples/simple_state.rs b/examples/simple_state.rs index 8f5be82375345457e67bb2680ab776cbfd65fe49..0e2e8e2d0768e06dae6320fd45dca1b90bb6e389 100644 --- a/examples/simple_state.rs +++ b/examples/simple_state.rs @@ -88,9 +88,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<CurrentCount, CurrentCountState>(); widget_context.add_widget_system( @@ -126,7 +126,8 @@ fn startup( </WindowContextProviderBundle> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs index 8df527c3ac4a7cb9f23ea5c882e1eea1de44bdcc..677c1e3abc280e0f198b5eb1dcccfdee365648aa 100644 --- a/examples/tabs/tabs.rs +++ b/examples/tabs/tabs.rs @@ -20,9 +20,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<Tab, EmptyState>(); widget_context.add_widget_data::<TabContextProvider, EmptyState>(); widget_context.add_widget_data::<TabButton, EmptyState>(); @@ -87,7 +87,7 @@ fn startup( </KayakAppBundle> } - commands.insert_resource(widget_context); + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/text.rs b/examples/text.rs index 3d6ae7eab5139c412d727463ae441c3917f20bbc..8b3a41428bd4eb2eaedd4742b2e738095c39e026 100644 --- a/examples/text.rs +++ b/examples/text.rs @@ -67,9 +67,8 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidgetProps, EmptyState>(); widget_context.add_widget_system( @@ -80,7 +79,8 @@ fn startup( rsx! { <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn update_resource(keyboard_input: Res<Input<KeyCode>>, mut my_resource: ResMut<MyResource>) { diff --git a/examples/text_box.rs b/examples/text_box.rs index 812b046fceb3287bd1f2a6cd8acdd05affe8f7f9..4894d90d772284f888a7fba1c02b492326a122b3 100644 --- a/examples/text_box.rs +++ b/examples/text_box.rs @@ -93,9 +93,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<TextBoxExample, TextBoxExampleState>(); widget_context.add_widget_system( @@ -119,7 +119,8 @@ fn startup( </WindowBundle> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs index 8d78df60949993cf5495aef0edaddf2512bb7fc2..87298e0a34b1487dd2983858fc842d589dc7a9e0 100644 --- a/examples/texture_atlas.rs +++ b/examples/texture_atlas.rs @@ -8,8 +8,6 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let image_handle = asset_server.load("texture_atlas.png"); //texture_atlas.png uses 16 pixel sprites and is 272x128 pixels @@ -31,6 +29,7 @@ fn startup( let flower_index = columns * 5 + 15; let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; let atlas_styles = KStyle { @@ -69,7 +68,7 @@ fn startup( </KayakAppBundle> } - commands.insert_resource(widget_context); + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs index 36687d9424d38083ae852198ee2443ca9de0adbf..3072de064177cf0e072df87b7adc5658d4a69bc5 100644 --- a/examples/todo/todo.rs +++ b/examples/todo/todo.rs @@ -57,9 +57,9 @@ fn startup( // Camera 2D forces a clear pass in bevy. // We do this because our scene is not rendering anything else. commands.spawn(Camera2dBundle::default()); - commands.spawn(UICameraBundle::new()); let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<TodoItemsProps, EmptyState>(); widget_context.add_widget_data::<TodoInputProps, EmptyState>(); @@ -97,7 +97,8 @@ fn startup( </WindowBundle> </KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/examples/vec.rs b/examples/vec.rs index 9e412a831ebb5d31228d5e75916a6ce51f6c0a18..4d9daf3316a2afd0a860358793400a86bde43b02 100644 --- a/examples/vec.rs +++ b/examples/vec.rs @@ -61,9 +61,8 @@ fn startup( ) { font_mapping.set_default(asset_server.load("roboto.kayak_font")); - commands.spawn(UICameraBundle::new()); - let mut widget_context = KayakRootContext::new(); + widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidgetProps, EmptyState>(); widget_context.add_widget_system( @@ -74,7 +73,8 @@ fn startup( rsx! { <KayakAppBundle><MyWidgetBundle /></KayakAppBundle> } - commands.insert_resource(widget_context); + + commands.spawn(UICameraBundle::new(widget_context)); } fn main() { diff --git a/src/calculate_nodes.rs b/src/calculate_nodes.rs index 5f37f783ad27d02b2e6c3025a59d59c55a39c4a7..569ebfe9d9fa696ac1b3da50d8cc03deaff9da2a 100644 --- a/src/calculate_nodes.rs +++ b/src/calculate_nodes.rs @@ -1,5 +1,5 @@ use bevy::{ - prelude::{Assets, Commands, Entity, Query, Res, ResMut, With}, + prelude::{Assets, Commands, Entity, In, Query, Res, With}, utils::HashMap, }; use kayak_font::KayakFont; @@ -15,14 +15,14 @@ use crate::{ }; pub fn calculate_nodes( + In(mut context): In<KayakRootContext>, mut commands: Commands, - mut context: ResMut<KayakRootContext>, fonts: Res<Assets<KayakFont>>, font_mapping: Res<FontMapping>, query: Query<Entity, With<DirtyNode>>, all_styles_query: Query<&KStyle>, node_query: Query<(Entity, &Node)>, -) { +) -> KayakRootContext { let mut new_nodes = HashMap::<Entity, (Node, bool)>::default(); // This is the maximum recursion depth for this method. // Recursion involves recalculating layout which should be done sparingly. @@ -39,11 +39,15 @@ pub fn calculate_nodes( // } if let Ok(tree) = context.tree.clone().try_read() { if tree.root_node.is_none() { - return; + return context; } for dirty_entity in query.iter() { let dirty_entity = WrappedIndex(dirty_entity); + if !tree.contains(dirty_entity) { + continue; + } + let styles = all_styles_query .get(dirty_entity.0) .unwrap_or(&default_styles); @@ -170,14 +174,15 @@ pub fn calculate_nodes( commands.entity(entity).insert(node); } } + + context } pub fn calculate_layout( + In(context): In<KayakRootContext>, mut commands: Commands, - mut context: ResMut<KayakRootContext>, nodes_no_entity_query: Query<&'static Node>, -) { - let context = context.as_mut(); +) -> KayakRootContext { if let Ok(tree) = context.tree.try_read() { // tree.dump(); let node_tree = &*tree; @@ -200,6 +205,8 @@ pub fn calculate_layout( } } } + + context } fn create_primitive( diff --git a/src/camera/camera.rs b/src/camera/camera.rs index 4f509abca81886413bc4551e9164efe241701bc0..f432acedb4b8a8aa7a0ea9deb165dd981dcb0c70 100644 --- a/src/camera/camera.rs +++ b/src/camera/camera.rs @@ -10,6 +10,8 @@ use bevy::{ }, }; +use crate::{context::KayakRootContext, event_dispatcher::EventDispatcher}; + use super::ortho::UIOrthographicProjection; /// Kayak UI's default UI camera. @@ -39,17 +41,19 @@ pub struct UICameraBundle { pub transform: Transform, pub global_transform: GlobalTransform, pub camera_ui: CameraUIKayak, + pub context: KayakRootContext, + pub event_disaptcher: EventDispatcher, } impl Default for UICameraBundle { fn default() -> Self { - Self::new() + Self::new(KayakRootContext::default()) } } impl UICameraBundle { pub const UI_CAMERA: &'static str = "KAYAK_UI_CAMERA"; - pub fn new() -> Self { + pub fn new(kayak_root_context: KayakRootContext) -> Self { // we want 0 to be "closest" and +far to be "farthest" in 2d, so we offset // the camera's translation by far and use a right handed coordinate system let far = 1000.0; @@ -85,6 +89,8 @@ impl UICameraBundle { camera_ui: CameraUIKayak { clear_color: ClearColorConfig::None, }, + context: kayak_root_context, + event_disaptcher: EventDispatcher::new(), } } } diff --git a/src/context.rs b/src/context.rs index f1bba5ef0b550e792c232d7ada4b49068e0a852c..cada0317ff75036aef4234015460b3ca8353601a 100644 --- a/src/context.rs +++ b/src/context.rs @@ -3,7 +3,7 @@ use std::sync::{Arc, RwLock}; use bevy::{ ecs::{event::ManualEventReader, system::CommandQueue}, prelude::*, - utils::HashMap, + utils::{HashMap, HashSet}, }; use morphorm::Hierarchy; @@ -14,6 +14,7 @@ use crate::{ context_entities::ContextEntities, event_dispatcher::EventDispatcher, focus_tree::FocusTree, + input::query_world, layout::{LayoutCache, Rect}, layout_dispatcher::LayoutEventDispatcher, node::{DirtyNode, WrappedIndex}, @@ -22,7 +23,7 @@ use crate::{ styles::KStyle, tree::{Change, Tree}, widget_state::WidgetState, - Focusable, WindowSize, + Focusable, KayakUIPlugin, WindowSize, }; /// A tag component representing when a widget has been mounted(added to the tree). @@ -58,7 +59,7 @@ type WidgetSystems = HashMap< /// }).id(); /// // Stores the kayak app widget in the widget context's tree. /// widget_context.add_widget(None, app_entity); -/// commands.insert_resource(widget_context); +/// commands.spawn(UICameraBundle::new(widget_context)); /// } /// /// fn main() { @@ -69,7 +70,7 @@ type WidgetSystems = HashMap< /// .add_startup_system(setup); /// } /// ``` -#[derive(Resource)] +#[derive(Component)] pub struct KayakRootContext { pub tree: Arc<RwLock<Tree>>, pub(crate) layout_cache: Arc<RwLock<LayoutCache>>, @@ -83,6 +84,7 @@ pub struct KayakRootContext { pub(crate) widget_state: WidgetState, pub(crate) order_tree: Arc<RwLock<Tree>>, pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>, + pub(crate) uninitilized_systems: HashSet<String>, } impl Default for KayakRootContext { @@ -107,9 +109,14 @@ impl KayakRootContext { widget_state: Default::default(), index: Default::default(), order_tree: Default::default(), + uninitilized_systems: Default::default(), } } + pub fn add_plugin(&mut self, plugin: impl KayakUIPlugin) { + plugin.build(self) + } + /// Get's the layout for th given widget index. pub(crate) fn get_layout(&self, id: &WrappedIndex) -> Option<Rect> { if let Ok(cache) = self.layout_cache.try_read() { @@ -141,10 +148,12 @@ impl KayakRootContext { update: impl IntoSystem<(KayakWidgetContext, Entity, Entity), bool, Params>, render: impl IntoSystem<(KayakWidgetContext, Entity), bool, Params2>, ) { + let type_name = type_name.into(); let update_system = Box::new(IntoSystem::into_system(update)); let render_system = Box::new(IntoSystem::into_system(render)); self.systems - .insert(type_name.into(), (update_system, render_system)); + .insert(type_name.clone(), (update_system, render_system)); + self.uninitilized_systems.insert(type_name); } /// Let's the widget context know what data types are used for a given widget. @@ -278,8 +287,6 @@ impl KayakRootContext { return vec![]; } - // self.node_tree.dump(); - let render_primitives = if let Ok(mut layout_cache) = self.layout_cache.try_write() { recurse_node_tree_to_build_primitives( &node_tree, @@ -346,12 +353,23 @@ fn recurse_node_tree_to_build_primitives( }; match &render_primitive { - RenderPrimitive::Text { content, .. } => { + RenderPrimitive::Text { + content, layout, .. + } => { log::trace!( - "Text node: {}-{} is equal to: {}", + "Text node: {}-{} is equal to: {}, {:?}", widget_names.get(current_node.0).unwrap().0, current_node.0.index(), content, + layout, + ); + } + RenderPrimitive::Clip { layout } => { + log::trace!( + "Clip node: {}-{} is equal to: {:?}", + widget_names.get(current_node.0).unwrap().0, + current_node.0.index(), + layout, ); } RenderPrimitive::Empty => { @@ -439,84 +457,99 @@ fn recurse_node_tree_to_build_primitives( } fn update_widgets_sys(world: &mut World) { - let mut context = world.remove_resource::<KayakRootContext>().unwrap(); - let tree_iterator = if let Ok(tree) = context.tree.read() { - tree.down_iter().collect::<Vec<_>>() - } else { - panic!("Failed to acquire read lock."); - }; - - // let change_tick = world.increment_change_tick(); + let mut context_data = Vec::new(); - let old_focus = if let Ok(mut focus_tree) = context.focus_tree.try_write() { - let current = focus_tree.current(); - focus_tree.clear(); - if let Ok(tree) = context.tree.read() { - if let Some(root_node) = tree.root_node { - focus_tree.add(root_node, &tree); + query_world::<Query<(Entity, &mut KayakRootContext)>, _, _>( + |mut query| { + for (entity, mut kayak_root_context) in query.iter_mut() { + context_data.push((entity, std::mem::take(&mut *kayak_root_context))); } - } - current - } else { - None - }; - - let mut new_ticks = HashMap::new(); - - // dbg!("Updating widgets!"); - update_widgets( + }, world, - &context.tree, - &context.layout_cache, - &mut context.systems, - tree_iterator, - &context.context_entities, - &context.focus_tree, - &context.clone_systems, - &context.cloned_widget_entities, - &context.widget_state, - &mut new_ticks, - &context.order_tree, - &context.index, ); - if let Some(old_focus) = old_focus { - if let Ok(mut focus_tree) = context.focus_tree.try_write() { - if focus_tree.contains(old_focus) { - focus_tree.focus(old_focus); + for (entity, mut context) in context_data.drain(..) { + for system_id in context.uninitilized_systems.drain() { + if let Some(system) = context.systems.get_mut(&system_id) { + system.0.initialize(world); + system.1.initialize(world); } } - } - // dbg!("Finished updating widgets!"); - let tick = world.read_change_tick(); + let tree_iterator = if let Ok(tree) = context.tree.read() { + tree.down_iter().collect::<Vec<_>>() + } else { + panic!("Failed to acquire read lock."); + }; + + // let change_tick = world.increment_change_tick(); - for (key, system) in context.systems.iter_mut() { - if let Some(new_tick) = new_ticks.get(key) { - system.0.set_last_change_tick(*new_tick); - system.1.set_last_change_tick(*new_tick); + let old_focus = if let Ok(mut focus_tree) = context.focus_tree.try_write() { + let current = focus_tree.current(); + focus_tree.clear(); + if let Ok(tree) = context.tree.read() { + if let Some(root_node) = tree.root_node { + focus_tree.add(root_node, &tree); + } + } + current } else { - system.0.set_last_change_tick(tick); - system.1.set_last_change_tick(tick); + None + }; + + let mut new_ticks = HashMap::new(); + + // dbg!("Updating widgets!"); + update_widgets( + world, + &context.tree, + &context.layout_cache, + &mut context.systems, + tree_iterator, + &context.context_entities, + &context.focus_tree, + &context.clone_systems, + &context.cloned_widget_entities, + &context.widget_state, + &mut new_ticks, + &context.order_tree, + &context.index, + ); + + if let Some(old_focus) = old_focus { + if let Ok(mut focus_tree) = context.focus_tree.try_write() { + if focus_tree.contains(old_focus) { + focus_tree.focus(old_focus); + } + } } - // system.apply_buffers(world); - } - // Clear out indices - if let Ok(mut indices) = context.index.try_write() { - // for (entity, value) in indices.iter_mut() { - // if tree.root_node.unwrap().0.id() != entity.id() { - // *value = 0; - // } - // } - indices.clear(); - } + // dbg!("Finished updating widgets!"); + let tick = world.read_change_tick(); - // if let Ok(order_tree) = context.order_tree.try_read() { - // order_tree.dump(); - // } + for (key, system) in context.systems.iter_mut() { + if let Some(new_tick) = new_ticks.get(key) { + system.0.set_last_change_tick(*new_tick); + system.1.set_last_change_tick(*new_tick); + } else { + system.0.set_last_change_tick(tick); + system.1.set_last_change_tick(tick); + } + // system.apply_buffers(world); + } + + // Clear out indices + if let Ok(mut indices) = context.index.try_write() { + // for (entity, value) in indices.iter_mut() { + // if tree.root_node.unwrap().0.id() != entity.id() { + // *value = 0; + // } + // } + indices.clear(); + } - world.insert_resource(context); + world.entity_mut(entity).insert(context); + } } fn update_widgets( @@ -623,7 +656,7 @@ fn update_widgets( tree.merge(&widget_context, *entity, diff, UPDATE_DEPTH); // if had_removal { - // tree.dump(); + // tree.dump(); // } for child in widget_context.child_iter(*entity) { @@ -708,7 +741,13 @@ fn update_widget( panic!("Couldn't get write lock!") }; - let widget_update_system = &mut systems.get_mut(&widget_type).unwrap().0; + let widget_update_system = &mut systems + .get_mut(&widget_type) + .expect(&format!( + "Wasn't able to find render/update systems for widget: {}!", + widget_type + )) + .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); @@ -857,16 +896,6 @@ fn update_widget( (widget_context, should_update_children) } -fn init_systems(world: &mut World) { - let mut context = world.remove_resource::<KayakRootContext>().unwrap(); - for system in context.systems.values_mut() { - system.0.initialize(world); - system.1.initialize(world); - } - - world.insert_resource(context); -} - /// The default Kayak Context plugin /// Creates systems and resources for kayak. pub struct KayakContextPlugin; @@ -877,7 +906,6 @@ 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(EventDispatcher::new()) .insert_resource(CustomEventReader(ManualEventReader::< bevy::window::CursorMoved, >::default())) @@ -896,7 +924,6 @@ impl Plugin for KayakContextPlugin { .add_plugin(crate::camera::KayakUICameraPlugin) .add_plugin(crate::render::BevyKayakUIRenderPlugin) .register_type::<Node>() - .add_startup_system_to_stage(StartupStage::PostStartup, init_systems.at_end()) .add_system_to_stage(CoreStage::Update, crate::input::process_events) .add_system_to_stage(CoreStage::PostUpdate, update_widgets_sys.at_start()) .add_system_to_stage(CoreStage::PostUpdate, calculate_ui.at_end()) @@ -906,30 +933,40 @@ impl Plugin for KayakContextPlugin { fn calculate_ui(world: &mut World) { // dbg!("Calculating nodes!"); - 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); + let mut context_data = Vec::new(); - for _ in 0..3 { - node_system.run((), world); - node_system.apply_buffers(world); + 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, + ); - layout_system.run((), world); - layout_system.apply_buffers(world); - world.resource_scope::<KayakRootContext, _>(|world, mut context| { - LayoutEventDispatcher::dispatch(&mut 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); - world.resource_scope::<KayakRootContext, _>(|world, mut context| { - world.resource_scope::<EventDispatcher, _>(|world, event_dispatcher| { - if event_dispatcher.hovered.is_none() { - context.current_cursor = CursorIcon::Default; - return; - } + for _ in 0..3 { + 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>() { @@ -937,15 +974,16 @@ fn calculate_ui(world: &mut World) { context.current_cursor = icon.0; } } - }); - if let Some(ref mut windows) = world.get_resource_mut::<Windows>() { - if let Some(window) = windows.get_primary_mut() { - window.set_cursor_icon(context.current_cursor); + if let Some(ref mut windows) = world.get_resource_mut::<Windows>() { + if let Some(window) = windows.get_primary_mut() { + window.set_cursor_icon(context.current_cursor); + } } } - }); + world.entity_mut(entity).insert((event_dispatcher, context)); + } // dbg!("Finished calculating nodes!"); // dbg!("Dispatching layout events!"); diff --git a/src/event_dispatcher.rs b/src/event_dispatcher.rs index 41d922785ceefc242192e835d033b12a545ebe5d..92679cd085a2a4a6b5cc6f390a2a69da34b603bd 100644 --- a/src/event_dispatcher.rs +++ b/src/event_dispatcher.rs @@ -1,5 +1,5 @@ use bevy::{ - prelude::{Entity, KeyCode, Resource, World}, + prelude::{Component, Entity, KeyCode, World}, utils::{HashMap, HashSet}, }; @@ -43,7 +43,7 @@ impl Default for EventState { } } -#[derive(Resource, Debug, Clone)] +#[derive(Component, Debug, Clone, Default)] pub struct EventDispatcher { is_mouse_pressed: bool, next_mouse_pressed: bool, @@ -177,11 +177,11 @@ impl EventDispatcher { /// Process and dispatch a set of [InputEvents](crate::InputEvent) pub(crate) fn process_events( &mut self, - input_events: Vec<InputEvent>, + input_events: &Vec<InputEvent>, context: &mut KayakRootContext, world: &mut World, ) { - let events = { self.build_event_stream(&input_events, context, world) }; + let events = { self.build_event_stream(input_events, context, world) }; self.dispatch_events(events, context, world); } diff --git a/src/input.rs b/src/input.rs index cf6f5ee0da84cd770d3c05206c602f248b5105f3..c798500d9346e9d47337ac71644a160370fd8bee 100644 --- a/src/input.rs +++ b/src/input.rs @@ -105,16 +105,35 @@ pub(crate) fn process_events(world: &mut World) { world, ); - world.resource_scope::<EventDispatcher, _>(|world, mut event_dispatcher| { - world.resource_scope::<KayakRootContext, _>(|world, mut context| { - event_dispatcher.process_events(input_events, &mut context, world); - }); - }); + // TODO: find a faster way of doing this. + 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, mut event_dispatcher, mut context) in context_data.drain(..) { + event_dispatcher.process_events(&input_events, &mut context, world); + + world.entity_mut(entity).insert((event_dispatcher, context)); + } } -fn query_world<T: bevy::ecs::system::SystemParam + 'static, F, R>(mut f: F, world: &mut World) -> R +pub(crate) fn query_world<T: bevy::ecs::system::SystemParam + 'static, F, R>( + f: F, + world: &mut World, +) -> R where - F: FnMut(<T::Fetch as bevy::ecs::system::SystemParamFetch<'_, '_>>::Item) -> R, + F: FnOnce(<T::Fetch as bevy::ecs::system::SystemParamFetch<'_, '_>>::Item) -> R, { let mut system_state = bevy::ecs::system::SystemState::<T>::new(world); let r = { diff --git a/src/lib.rs b/src/lib.rs index 541553089de76b2996129c687039bcf45ebc1c1e..3af641501c0722340bf7f21038dfb47d5c4f3947 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,7 @@ mod widget_state; pub mod widgets; mod window_size; +use context::KayakRootContext; pub use window_size::WindowSize; pub use camera::*; @@ -63,3 +64,7 @@ pub mod prelude { } pub use focus_tree::Focusable; + +pub trait KayakUIPlugin { + fn build(&self, context: &mut KayakRootContext); +} diff --git a/src/render/extract.rs b/src/render/extract.rs index 8312995bceb85f40d015104cc376003d904a4af1..5f85576dd4d3185db07de440cca2782d4c5419d5 100644 --- a/src/render/extract.rs +++ b/src/render/extract.rs @@ -5,7 +5,7 @@ use crate::{ styles::Corner, }; use bevy::{ - prelude::{Assets, Color, Commands, Image, Plugin, Query, Rect, Res, Vec2}, + prelude::{Assets, Camera, Color, Commands, Image, Plugin, Query, Rect, Res, Vec2}, render::{Extract, RenderApp, RenderStage}, window::Windows, }; @@ -31,7 +31,7 @@ impl Plugin for BevyKayakUIExtractPlugin { pub fn extract( mut commands: Commands, - context: Extract<Res<KayakRootContext>>, + context_query: Extract<Query<(&KayakRootContext, &Camera)>>, fonts: Extract<Res<Assets<KayakFont>>>, font_mapping: Extract<Res<FontMapping>>, node_query: Extract<Query<&Node>>, @@ -39,20 +39,24 @@ pub fn extract( images: Extract<Res<Assets<Image>>>, windows: Extract<Res<Windows>>, ) { - // dbg!("STARTED"); - let render_primitives = context.build_render_primitives(&node_query, &widget_names); - // dbg!("FINISHED"); - - let dpi = if let Some(window) = windows.get_primary() { - window.scale_factor() as f32 - } else { - 1.0 - }; - - // dbg!(&render_primitives); + let mut render_primitives = Vec::new(); + for (context, camera) in context_query.iter() { + let dpi = match &camera.target { + bevy::render::camera::RenderTarget::Window(window_id) => { + if let Some(window) = windows.get(*window_id) { + window.scale_factor() as f32 + } else { + 1.0 + } + } + _ => 1.0, + }; + let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names); + render_primitives.extend(new_render_primitives.drain(..).map(|r| (dpi, r))); + } let mut extracted_quads = Vec::new(); - for render_primitive in render_primitives { + for (dpi, render_primitive) in render_primitives { match render_primitive { RenderPrimitive::Text { .. } => { let text_quads = font::extract_texts(&render_primitive, &fonts, &font_mapping, dpi); diff --git a/src/render/font/mod.rs b/src/render/font/mod.rs index 39515cb5e9b037184b24b7c4c797c51a3a3c88c8..86e02cb7a3fa388c1405cb3bf9ca3da4072cbb1f 100644 --- a/src/render/font/mod.rs +++ b/src/render/font/mod.rs @@ -1,5 +1,4 @@ -use bevy::prelude::{Assets, Plugin, Res, ResMut}; -use kayak_font::KayakFont; +use bevy::prelude::{Added, Entity, Plugin, Query, ResMut}; mod extract; mod font_mapping; @@ -21,13 +20,9 @@ impl Plugin for TextRendererPlugin { fn process_loaded_fonts( mut font_mapping: ResMut<FontMapping>, - _fonts: Res<Assets<KayakFont>>, - context_resource: Res<KayakRootContext>, + context_query: Query<Entity, Added<KayakRootContext>>, ) { - // if let Some(context = context_resource.as_ref() { - if context_resource.is_added() { + for _ in context_query.iter() { font_mapping.mark_all_as_new(); } - // font_mapping.add_loaded_to_kayak(&fonts, &context); - // } } diff --git a/src/widgets/app.rs b/src/widgets/app.rs index 7227042fdb5fbdd28598229f2346bcaaefec6087..4626c2a6377cae135c3adb5815587007b4571100 100644 --- a/src/widgets/app.rs +++ b/src/widgets/app.rs @@ -64,7 +64,6 @@ pub fn app_update( pub fn app_render( In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, mut commands: Commands, - windows: Res<Windows>, mut query: Query<(&mut KStyle, &KChildren)>, camera: Query<&Camera, With<CameraUIKayak>>, ) -> bool { @@ -77,12 +76,6 @@ pub fn app_render( } } - if width == 0.0 { - let primary_window = windows.get_primary().unwrap(); - width = primary_window.width(); - height = primary_window.height(); - } - if let Ok((mut app_style, children)) = query.get_mut(entity) { if app_style.width != StyleProp::Value(Units::Pixels(width)) { app_style.width = StyleProp::Value(Units::Pixels(width)); @@ -90,8 +83,8 @@ pub fn app_render( if app_style.height != StyleProp::Value(Units::Pixels(height)) { app_style.height = StyleProp::Value(Units::Pixels(height)); } + app_style.render_command = StyleProp::Value(RenderCommand::Layout); - // children.process(&widget_context, Some(entity)); let parent_id = Some(entity); rsx! { <ClipBundle diff --git a/src/widgets/mod.rs b/src/widgets/mod.rs index 2ab6866681c67f1dc026ec82f3323b9f1a6251a2..96825c57566eb94ac0e9a29a8733e0a8a1e310dd 100644 --- a/src/widgets/mod.rs +++ b/src/widgets/mod.rs @@ -78,6 +78,7 @@ use window::window_render; use crate::{ context::KayakRootContext, widget::{widget_update, widget_update_with_context, EmptyState, Widget}, + KayakUIPlugin, }; use self::window_context_provider::window_context_render; @@ -86,103 +87,106 @@ pub struct KayakWidgets; impl Plugin for KayakWidgets { fn build(&self, app: &mut bevy::prelude::App) { - app.add_startup_system_to_stage(StartupStage::PostStartup, add_widget_systems) - .add_system(text_box::cursor_animation_system); + app.add_system(text_box::cursor_animation_system); } } -fn add_widget_systems(mut context: ResMut<KayakRootContext>) { - context.add_widget_data::<KayakApp, EmptyState>(); - context.add_widget_data::<KButton, ButtonState>(); - context.add_widget_data::<TextProps, EmptyState>(); - context.add_widget_data::<KWindow, KWindowState>(); - context.add_widget_data::<WindowContextProvider, EmptyState>(); - context.add_widget_data::<Background, EmptyState>(); - context.add_widget_data::<Clip, EmptyState>(); - context.add_widget_data::<KImage, EmptyState>(); - context.add_widget_data::<TextureAtlasProps, EmptyState>(); - context.add_widget_data::<NinePatch, EmptyState>(); - context.add_widget_data::<Element, EmptyState>(); - context.add_widget_data::<ScrollBarProps, EmptyState>(); - context.add_widget_data::<ScrollContentProps, EmptyState>(); - context.add_widget_data::<ScrollBoxProps, EmptyState>(); - context.add_widget_data::<ScrollContextProvider, EmptyState>(); - context.add_widget_data::<TextBoxProps, TextBoxState>(); +pub struct KayakWidgetsContextPlugin; - context.add_widget_system(KayakApp::default().get_name(), app_update, app_render); - context.add_widget_system( - KButton::default().get_name(), - widget_update::<KButton, ButtonState>, - button_render, - ); - context.add_widget_system( - TextProps::default().get_name(), - widget_update::<TextProps, EmptyState>, - text_render, - ); - context.add_widget_system( - WindowContextProvider::default().get_name(), - widget_update::<WindowContextProvider, EmptyState>, - window_context_render, - ); - context.add_widget_system( - KWindow::default().get_name(), - widget_update_with_context::<KWindow, KWindowState, WindowContext>, - window_render, - ); - context.add_widget_system( - Background::default().get_name(), - widget_update::<Background, EmptyState>, - background_render, - ); - context.add_widget_system( - Clip::default().get_name(), - widget_update::<Clip, EmptyState>, - clip_render, - ); - context.add_widget_system( - KImage::default().get_name(), - widget_update::<KImage, EmptyState>, - image_render, - ); - context.add_widget_system( - TextureAtlasProps::default().get_name(), - widget_update::<TextureAtlasProps, EmptyState>, - texture_atlas_render, - ); - context.add_widget_system( - NinePatch::default().get_name(), - widget_update::<NinePatch, EmptyState>, - nine_patch_render, - ); - context.add_widget_system( - Element::default().get_name(), - widget_update::<Element, EmptyState>, - element_render, - ); - context.add_widget_system( - ScrollBarProps::default().get_name(), - widget_update_with_context::<ScrollBarProps, EmptyState, ScrollContext>, - scroll_bar_render, - ); - context.add_widget_system( - ScrollContentProps::default().get_name(), - widget_update_with_context::<ScrollContentProps, EmptyState, ScrollContext>, - scroll_content_render, - ); - context.add_widget_system( - ScrollBoxProps::default().get_name(), - widget_update_with_context::<ScrollBoxProps, EmptyState, ScrollContext>, - scroll_box_render, - ); - context.add_widget_system( - ScrollContextProvider::default().get_name(), - widget_update::<ScrollContextProvider, EmptyState>, - scroll_context_render, - ); - context.add_widget_system( - TextBoxProps::default().get_name(), - widget_update::<TextBoxProps, TextBoxState>, - text_box_render, - ); +impl KayakUIPlugin for KayakWidgetsContextPlugin { + fn build(&self, context: &mut KayakRootContext) { + context.add_widget_data::<KayakApp, EmptyState>(); + context.add_widget_data::<KButton, ButtonState>(); + context.add_widget_data::<TextProps, EmptyState>(); + context.add_widget_data::<KWindow, KWindowState>(); + context.add_widget_data::<WindowContextProvider, EmptyState>(); + context.add_widget_data::<Background, EmptyState>(); + context.add_widget_data::<Clip, EmptyState>(); + context.add_widget_data::<KImage, EmptyState>(); + context.add_widget_data::<TextureAtlasProps, EmptyState>(); + context.add_widget_data::<NinePatch, EmptyState>(); + context.add_widget_data::<Element, EmptyState>(); + context.add_widget_data::<ScrollBarProps, EmptyState>(); + context.add_widget_data::<ScrollContentProps, EmptyState>(); + context.add_widget_data::<ScrollBoxProps, EmptyState>(); + context.add_widget_data::<ScrollContextProvider, EmptyState>(); + context.add_widget_data::<TextBoxProps, TextBoxState>(); + + context.add_widget_system(KayakApp::default().get_name(), app_update, app_render); + context.add_widget_system( + KButton::default().get_name(), + widget_update::<KButton, ButtonState>, + button_render, + ); + context.add_widget_system( + TextProps::default().get_name(), + widget_update::<TextProps, EmptyState>, + text_render, + ); + context.add_widget_system( + WindowContextProvider::default().get_name(), + widget_update::<WindowContextProvider, EmptyState>, + window_context_render, + ); + context.add_widget_system( + KWindow::default().get_name(), + widget_update_with_context::<KWindow, KWindowState, WindowContext>, + window_render, + ); + context.add_widget_system( + Background::default().get_name(), + widget_update::<Background, EmptyState>, + background_render, + ); + context.add_widget_system( + Clip::default().get_name(), + widget_update::<Clip, EmptyState>, + clip_render, + ); + context.add_widget_system( + KImage::default().get_name(), + widget_update::<KImage, EmptyState>, + image_render, + ); + context.add_widget_system( + TextureAtlasProps::default().get_name(), + widget_update::<TextureAtlasProps, EmptyState>, + texture_atlas_render, + ); + context.add_widget_system( + NinePatch::default().get_name(), + widget_update::<NinePatch, EmptyState>, + nine_patch_render, + ); + context.add_widget_system( + Element::default().get_name(), + widget_update::<Element, EmptyState>, + element_render, + ); + context.add_widget_system( + ScrollBarProps::default().get_name(), + widget_update_with_context::<ScrollBarProps, EmptyState, ScrollContext>, + scroll_bar_render, + ); + context.add_widget_system( + ScrollContentProps::default().get_name(), + widget_update_with_context::<ScrollContentProps, EmptyState, ScrollContext>, + scroll_content_render, + ); + context.add_widget_system( + ScrollBoxProps::default().get_name(), + widget_update_with_context::<ScrollBoxProps, EmptyState, ScrollContext>, + scroll_box_render, + ); + context.add_widget_system( + ScrollContextProvider::default().get_name(), + widget_update::<ScrollContextProvider, EmptyState>, + scroll_context_render, + ); + context.add_widget_system( + TextBoxProps::default().get_name(), + widget_update::<TextBoxProps, TextBoxState>, + text_box_render, + ); + } }