diff --git a/Cargo.toml b/Cargo.toml index f39ff22ffcda0ef928885a1518e04f3e33359ce7..6413a505c0f78256c7d138a02170f32278cad381 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,6 @@ resolver = "2" authors = ["John Mitchell"] homepage = "https://github.com/StarArawn/kayak_ui" repository = "https://github.com/StarArawn/kayak_ui" -license = "MIT OR Apache-2.0" license-file = "LICENSE" exclude = ["assets/*", "screenshots/*", "book"] @@ -17,7 +16,7 @@ members = ["kayak_ui_macros", "kayak_font"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bevy = { version = "0.9", default-features = false, features = ["bevy_ui"] } +bevy = { version = "0.10", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_winit", "bevy_core_pipeline", "bevy_ui"] } bytemuck = "1.12" dashmap = "5.4" kayak_font = { path = "./kayak_font", version = "0.2" } @@ -32,7 +31,8 @@ instant = "0.1" [dev-dependencies] fastrand = "1.8" -bevy-inspector-egui = "0.14" +bevy-inspector-egui = "0.18" +bevy = { version = "0.10", default-features = true } [[example]] name = "tabs" diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs index bf8439b69e0f8639de094acb0df5cb2941d37c06..7750894247a12d778a4bd99787adb15e9e6691c7 100644 --- a/examples/bevy_scene.rs +++ b/examples/bevy_scene.rs @@ -1,6 +1,7 @@ use bevy::{ math::{Vec3Swizzles, Vec4Swizzles}, prelude::*, + window::PrimaryWindow, }; use kayak_ui::prelude::{widgets::*, *}; @@ -40,7 +41,7 @@ fn set_active_tile_target( cursor: Res<Input<MouseButton>>, event_context: Query<&EventDispatcher, With<GameUI>>, camera_transform: Query<&GlobalTransform, With<WorldCamera>>, - windows: Res<Windows>, + window: Query<&Window, With<PrimaryWindow>>, ) { if !cursor.just_pressed(MouseButton::Left) { // Only run this system when the mouse button is clicked @@ -62,7 +63,7 @@ fn set_active_tile_target( // } // ``` - let world_pos = cursor_to_world(&windows, camera_transform.single()); + let world_pos = cursor_to_world(window.single(), camera_transform.single()); let tile_pos = world_to_tile(world_pos); let mut tile = tile.single_mut(); tile.target = tile_pos; @@ -83,11 +84,11 @@ fn move_ghost_tile( mut tile: Query<&mut Transform, With<GhostTile>>, mut cursor_moved: EventReader<CursorMoved>, camera_transform: Query<&GlobalTransform, With<WorldCamera>>, - windows: Res<Windows>, + window: Query<&Window, With<PrimaryWindow>>, ) { for _ in cursor_moved.iter() { if !event_context.single().contains_cursor() { - let world_pos = cursor_to_world(&windows, camera_transform.single()); + let world_pos = cursor_to_world(window.single(), camera_transform.single()); let tile_pos = world_to_tile(world_pos); let mut ghost = tile.single_mut(); ghost.translation.x = tile_pos.x; @@ -115,7 +116,6 @@ fn on_color_change( /// A system that sets up the world fn world_setup(mut commands: Commands, active_color: Res<ActiveColor>) { - commands.spawn((Camera2dBundle::default(), WorldCamera)); commands .spawn(SpriteBundle { sprite: Sprite { @@ -139,8 +139,7 @@ fn world_setup(mut commands: Commands, active_color: Res<ActiveColor>) { } /// Get the world position of the cursor in 2D space -fn cursor_to_world(windows: &Windows, camera_transform: &GlobalTransform) -> Vec2 { - let window = windows.get_primary().unwrap(); +fn cursor_to_world(window: &Window, camera_transform: &GlobalTransform) -> Vec2 { let size = Vec2::new(window.width(), window.height()); let mut pos = window.cursor_position().unwrap_or_default(); @@ -172,16 +171,22 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + // The UI Camera and the world camera are the same. + // CameraUIKayak is used to tell kayak which camera should render UI. + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak, WorldCamera)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let handle_change_color = OnEvent::new( move |In((event_dispatcher_context, _, event, _)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut active_color: ResMut<ActiveColor>| { @@ -255,7 +260,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn((UICameraBundle::new(widget_context), GameUI)); + commands.spawn((widget_context, EventDispatcher::default(), GameUI)); } fn main() { diff --git a/examples/clipping.rs b/examples/clipping.rs index 9eeb05889354fa863a2d7b63766892ccd9a29d31..36203347a6d4db0e83e6327e7f172fa7f84489c9 100644 --- a/examples/clipping.rs +++ b/examples/clipping.rs @@ -6,11 +6,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("lato-light.kttf")); let image = asset_server.load("panel.png"); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -56,7 +60,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/conditional_widget.rs b/examples/conditional_widget.rs index 1791f087cf45762443f3032586ee3d1355cb1c98..20d9c341212cfaac5062d668f750e267d59cd7b0 100644 --- a/examples/conditional_widget.rs +++ b/examples/conditional_widget.rs @@ -48,7 +48,7 @@ fn my_widget_render( text: "Show Window".into(), }} on_event={OnEvent::new( - move |In((event_dispatcher_context, _, mut event, _entity)): In<(EventDispatcherContext, WidgetState, Event, Entity)>, + move |In((event_dispatcher_context, _, mut event, _entity)): In<(EventDispatcherContext, WidgetState, KEvent, Entity)>, mut query: Query<&mut MyWidgetState>| { event.prevent_default(); event.stop_propagation(); @@ -78,7 +78,7 @@ fn my_widget_render( <KButtonBundle button={KButton { text: "Hide Window".into(), ..Default::default() }} on_event={OnEvent::new( - move |In((event_dispatcher_context, _, mut event, _entity)): In<(EventDispatcherContext, WidgetState, Event, Entity)>, + move |In((event_dispatcher_context, _, mut event, _entity)): In<(EventDispatcherContext, WidgetState, KEvent, Entity)>, mut query: Query<&mut MyWidgetState>| { match event.event_type { EventType::Click(..) => { @@ -109,13 +109,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("lato-light.kttf")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("lato-light.kttf")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidget, MyWidgetState>(); @@ -130,7 +130,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/context.rs b/examples/context.rs index a712519772efeb7726587817b1727e9256014ac3..9c4603c16fe7908ba60fd7b823e4674537489946 100644 --- a/examples/context.rs +++ b/examples/context.rs @@ -104,7 +104,7 @@ fn update_theme_button( move |In((event_dispatcher_context, _, event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, query: Query<&ThemeButton>, @@ -325,13 +325,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<ThemeDemo, EmptyState>(); widget_context.add_widget_data::<ThemeButton, EmptyState>(); @@ -377,7 +377,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/demo.rs b/examples/demo.rs index 0ba501e2ec1b438fcfab777ade91007f1ae7384e..0d562a49bf41bc761f641ea04122782a6d4da336 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -21,7 +21,11 @@ fn my_widget_1_render( impl Widget for MyWidget {} fn startup(mut commands: Commands) { - let mut context = KayakRootContext::new(); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + + let mut context = KayakRootContext::new(camera_entity); context.add_plugin(KayakWidgetsContextPlugin); context.add_widget_system( MyWidget::default().get_name(), @@ -47,7 +51,7 @@ fn startup(mut commands: Commands) { }); context.add_widget(None, app_entity); - commands.spawn(UICameraBundle::new(context)); + commands.spawn((context, EventDispatcher::default())); } // 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 caf0660553c22cf932aa72ebfb63fcb1eafefffc..188504eb9d5bc0316036b79bbd7c15951b9175c8 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -1,14 +1,23 @@ use bevy::prelude::*; -use kayak_ui::prelude::{widgets::*, *}; +use kayak_ui::{ + prelude::{widgets::*, *}, + CameraUIKayak, +}; fn startup( mut commands: Commands, mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn(Camera2dBundle::default()) + .insert(CameraUIKayak) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); + // font_mapping.force_subpixel(&asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -23,7 +32,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs index 1b472f8eb962ed39ece9044244278655b1d0005b..7216668abb4043f7a24c896ab08fc824fb5149b2 100644 --- a/examples/hello_world_no_macro.rs +++ b/examples/hello_world_no_macro.rs @@ -5,8 +5,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn(Camera2dBundle::default()) + .insert(CameraUIKayak) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let app_entity = widget_context.spawn_widget(&mut commands, None); @@ -39,7 +44,7 @@ fn startup( // Add widget context as resource. - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { App::new() diff --git a/examples/image.rs b/examples/image.rs index ae984f6ebe19cee77666e543b6127fd89889414d..40c7edba142927b072af1cfeaf7e3963e994ba5b 100644 --- a/examples/image.rs +++ b/examples/image.rs @@ -6,11 +6,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); let image = asset_server.load("generic-rpg-vendor.png"); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -30,7 +34,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/layout.rs b/examples/layout.rs index 6fdc489262656d4c4e1dc796f472809824ce5572..f2a0e842befff80835b0279686ac37c149352099 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -6,13 +6,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -130,7 +130,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/main_menu.rs b/examples/main_menu.rs index 40949d683ca0689499637a650580f8fef8e79df8..4f2d649fc3baa96b580a3b332b0b591368ae1996 100644 --- a/examples/main_menu.rs +++ b/examples/main_menu.rs @@ -49,7 +49,7 @@ fn menu_button_render( move |In((event_dispatcher_context, _, mut event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut ButtonState>| { @@ -123,9 +123,13 @@ fn startup( asset_server: Res<AssetServer>, mut preload_resource: ResMut<PreloadResource>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("lato-light.kttf")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<MenuButton, ButtonState>(); widget_context.add_widget_system( @@ -151,7 +155,7 @@ fn startup( move |In((event_dispatcher_context, _, event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut exit: EventWriter<AppExit>| { @@ -218,7 +222,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/multi_context.rs b/examples/multi_context.rs deleted file mode 100644 index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000 --- a/examples/multi_context.rs +++ /dev/null @@ -1 +0,0 @@ - diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs index 1aeeadaa5adaf3abacb88cfa9ebdf8ea3f856847..ea211a725baacca64ccb2df3f49c97496d85a64a 100644 --- a/examples/nine_patch.rs +++ b/examples/nine_patch.rs @@ -6,11 +6,15 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); let image = asset_server.load("panel.png"); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -51,7 +55,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/quads.rs b/examples/quads.rs index 33216d627d9568f50e24debf88b7025ce2b9c690..771b6a211c0e59a55c5e2d83180a5e40f54bad0a 100644 --- a/examples/quads.rs +++ b/examples/quads.rs @@ -39,7 +39,7 @@ fn my_quad_update( move |In((event_dispatcher_context, _, mut event, entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<(&mut KStyle, &MyQuad)>| { @@ -94,13 +94,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_system( MyQuad::default().get_name(), @@ -134,7 +134,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/render_target.rs b/examples/render_target.rs index d17bb3ba89908ac328ccd59f7c5ae8da88b5e3c3..e6ee73f9e5a4af1955cd62664ed9c68ddfc198c6 100644 --- a/examples/render_target.rs +++ b/examples/render_target.rs @@ -41,6 +41,7 @@ fn startup( label: None, size, dimension: TextureDimension::D2, + view_formats: &[TextureFormat::Bgra8UnormSrgb], format: TextureFormat::Bgra8UnormSrgb, mip_level_count: 1, sample_count: 1, @@ -56,7 +57,22 @@ fn startup( let image_handle = images.add(image); - let mut widget_context = KayakRootContext::new(); + let camera_entity = commands + .spawn(Camera2dBundle { + camera: Camera { + priority: -1, + target: RenderTarget::Image(image_handle.clone()), + ..Camera::default() + }, + camera_2d: Camera2d { + clear_color: bevy::core_pipeline::clear_color::ClearColorConfig::Default, + }, + ..Default::default() + }) + .insert(CameraUIKayak) + .id(); + + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -81,18 +97,7 @@ fn startup( /> </KayakAppBundle> }; - - 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) - }); + commands.spawn((widget_context, EventDispatcher::default())); // Setup 3D scene // Light @@ -127,14 +132,17 @@ fn startup( .insert(MainPassCube); // The main pass camera. - commands.spawn(Camera3dBundle { - transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) - .looking_at(Vec3::default(), Vec3::Y), - ..default() - }); + let camera_entity = commands + .spawn(Camera3dBundle { + transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) + .looking_at(Vec3::default(), Vec3::Y), + ..default() + }) + .insert(CameraUIKayak) + .id(); // Spawn another UI in 2D space! - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -148,7 +156,7 @@ fn startup( /> </KayakAppBundle> }; - commands.spawn((UICameraBundle::new(widget_context), MainUI)); + commands.spawn((widget_context, EventDispatcher::default(), MainUI)); } /// Rotates the outer cube (main pass) diff --git a/examples/scrolling.rs b/examples/scrolling.rs index cac9c08d53e7aae9a0a4b3ff770dea080a243241..d77fa08a432ae3675ba93899b6bd621863c8c9b1 100644 --- a/examples/scrolling.rs +++ b/examples/scrolling.rs @@ -6,6 +6,10 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + let font_asset = asset_server.load("roboto.kayak_font"); font_mapping.set_default(font_asset.clone()); @@ -14,11 +18,7 @@ fn startup( // will be ignored if this setting is used. font_mapping.force_subpixel(&font_asset); - // 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(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -60,7 +60,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/simple_state.rs b/examples/simple_state.rs index 2e27896d14c468df56b2b3aae131db2160cbb8e2..82e2c28ab7b8c922e73582a5045b804527e0f494 100644 --- a/examples/simple_state.rs +++ b/examples/simple_state.rs @@ -57,7 +57,7 @@ fn current_count_render( ..Default::default() }} on_event={OnEvent::new( - move |In((event_dispatcher_context, _, mut event, _entity)): In<(EventDispatcherContext, WidgetState, Event, Entity)>, + move |In((event_dispatcher_context, _, mut event, _entity)): In<(EventDispatcherContext, WidgetState, KEvent, Entity)>, mut query: Query<&mut CurrentCountState>| { match event.event_type { EventType::Click(..) => { @@ -85,13 +85,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("lato-light.kttf")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("lato-light.kttf")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<CurrentCount, CurrentCountState>(); @@ -129,7 +129,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/tabs/tab_button.rs b/examples/tabs/tab_button.rs index 2741684da64753d9b4660908fb7a0e5897b47800..811a515f7854e1e15d12a20d120ffbcdd9c57a88 100644 --- a/examples/tabs/tab_button.rs +++ b/examples/tabs/tab_button.rs @@ -1,7 +1,7 @@ use bevy::prelude::{Bundle, Color, Commands, Component, Entity, In, Query}; use kayak_ui::{ prelude::{ - rsx, widgets::KButtonBundle, Corner, Event, EventDispatcherContext, EventType, KStyle, + rsx, widgets::KButtonBundle, Corner, EventDispatcherContext, EventType, KEvent, KStyle, KayakWidgetContext, OnEvent, StyleProp, Units, Widget, WidgetName, WidgetState, }, widgets::KButton, @@ -57,7 +57,7 @@ pub fn tab_button_render( move |In((event_dispatcher_context, _, event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut TabContext>| { diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs index 2539aecd9720908d149348c467003415284a2210..9ecb4b53a67c422678a65e0c1255a5cd25c1d3b8 100644 --- a/examples/tabs/tabs.rs +++ b/examples/tabs/tabs.rs @@ -15,13 +15,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<Tab, EmptyState>(); widget_context.add_widget_data::<TabContextProvider, EmptyState>(); @@ -87,7 +87,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/test_no_startup.rs b/examples/test_no_startup.rs index 060081ee9ab9b6337605abc4f220d4ebcf4b9460..503a4ed738ae95249df095724729591ecabeb240 100644 --- a/examples/test_no_startup.rs +++ b/examples/test_no_startup.rs @@ -1,15 +1,15 @@ use bevy::prelude::*; use kayak_ui::prelude::{widgets::*, *}; -#[derive(Default, Clone, Copy, PartialEq, Hash, Eq, Debug)] +#[derive(Default, Clone, Copy, PartialEq, Hash, Eq, Debug, States)] pub enum GameState { #[default] First, Second, } -fn first_sys(mut state: ResMut<State<GameState>>) { - state.overwrite_replace(GameState::Second).unwrap(); +fn first_sys(mut state: ResMut<NextState<GameState>>) { + state.set(GameState::Second); } fn second_sys( @@ -17,9 +17,13 @@ fn second_sys( asset_server: Res<AssetServer>, mut font_mapping: ResMut<FontMapping>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; rsx! { @@ -34,7 +38,7 @@ fn second_sys( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { @@ -42,8 +46,8 @@ fn main() { .add_plugins(DefaultPlugins) .add_plugin(KayakContextPlugin) .add_plugin(KayakWidgets) - .add_state(GameState::First) - .add_system_set(SystemSet::on_enter(GameState::First).with_system(first_sys)) - .add_system_set(SystemSet::on_enter(GameState::Second).with_system(second_sys)) - .run() + .add_state::<GameState>() + .add_system(first_sys.in_schedule(OnEnter(GameState::First))) + .add_system(second_sys.in_schedule(OnEnter(GameState::Second))) + .run(); } diff --git a/examples/text.rs b/examples/text.rs index fa49287c6f47b86904eb8466d09b5a13c6af626d..496023d6d5d3fc8ea978f5c25a687ae311360fe3 100644 --- a/examples/text.rs +++ b/examples/text.rs @@ -73,13 +73,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidgetProps, EmptyState>(); @@ -92,7 +92,7 @@ fn startup( <KayakAppBundle><MyWidgetBundle props={MyWidgetProps { foo: 0 }} /></KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } 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 72b273156600b77fc3f2ef3436bca5bb6a02d0b0..edb8e9f48deb69bf07f60bd2991ca9fd53a29ff2 100644 --- a/examples/text_box.rs +++ b/examples/text_box.rs @@ -88,13 +88,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<TextBoxExample, TextBoxExampleState>(); @@ -120,7 +120,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs index d4e311ae9fa9825ef10fe6e3659aefc5d7239ff9..69f7d7d99ae1009bb6e87bed46684f40f0612fde 100644 --- a/examples/texture_atlas.rs +++ b/examples/texture_atlas.rs @@ -6,6 +6,10 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); let image_handle = asset_server.load("texture_atlas.png"); @@ -28,7 +32,7 @@ fn startup( //The flower is in the 6(-1) row and 15 collumn let flower_index = columns * 5 + 15; - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; @@ -68,7 +72,7 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/examples/todo/input.rs b/examples/todo/input.rs index 38b2da700a6496d85b156601cb60c5bb3bebd66f..37017d93eba74b1b6765ae853405db4513cf9f38 100644 --- a/examples/todo/input.rs +++ b/examples/todo/input.rs @@ -52,7 +52,7 @@ pub fn render_todo_input( move |In((event_dispatcher_context, _, event, _)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut todo_list: ResMut<TodoList>| { diff --git a/examples/todo/items.rs b/examples/todo/items.rs index 8892ece69b5ed7c0f03d91931dcb48e62a4477db..d7adac9023e02f21756864ebf565b8191061ad1d 100644 --- a/examples/todo/items.rs +++ b/examples/todo/items.rs @@ -50,7 +50,7 @@ pub fn render_todo_items( move |In((event_dispatcher_context, _, event, _)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut todo_list: ResMut<TodoList>,| { diff --git a/examples/todo/todo.rs b/examples/todo/todo.rs index 4f62b8823c5794297a8394d45e1e86dc80e23152..66b12c2b7d34fd2acc04cfdb90424178519f2af1 100644 --- a/examples/todo/todo.rs +++ b/examples/todo/todo.rs @@ -1,5 +1,5 @@ use bevy::prelude::*; -use bevy_inspector_egui::WorldInspectorPlugin; +use bevy_inspector_egui::quick::WorldInspectorPlugin; use kayak_ui::prelude::{widgets::*, *}; mod input; @@ -53,13 +53,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { - font_mapping.set_default(asset_server.load("roboto.kayak_font")); + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); - // Camera 2D forces a clear pass in bevy. - // We do this because our scene is not rendering anything else. - commands.spawn(Camera2dBundle::default()); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); widget_context.add_widget_data::<TodoItemsProps, EmptyState>(); widget_context.add_widget_data::<TodoInputProps, EmptyState>(); @@ -99,17 +99,17 @@ fn startup( </KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { App::new() .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) - .add_plugin(WorldInspectorPlugin::new()) + .add_plugin(WorldInspectorPlugin::default()) .add_plugin(KayakContextPlugin) .add_plugin(KayakWidgets) - .insert_non_send_resource(TodoList::new()) + .insert_resource(TodoList::new()) .add_startup_system(startup) .run() } diff --git a/examples/vec.rs b/examples/vec.rs index fa362a71ca9d2e76f780a8cbab998a538b3c030f..459e579371b2246ae5614c8253f969aa401ca12f 100644 --- a/examples/vec.rs +++ b/examples/vec.rs @@ -57,9 +57,13 @@ fn startup( mut font_mapping: ResMut<FontMapping>, asset_server: Res<AssetServer>, ) { + let camera_entity = commands + .spawn((Camera2dBundle::default(), CameraUIKayak)) + .id(); + font_mapping.set_default(asset_server.load("roboto.kayak_font")); - let mut widget_context = KayakRootContext::new(); + let mut widget_context = KayakRootContext::new(camera_entity); widget_context.add_plugin(KayakWidgetsContextPlugin); let parent_id = None; widget_context.add_widget_data::<MyWidgetProps, EmptyState>(); @@ -72,7 +76,7 @@ fn startup( <KayakAppBundle><MyWidgetBundle /></KayakAppBundle> }; - commands.spawn(UICameraBundle::new(widget_context)); + commands.spawn((widget_context, EventDispatcher::default())); } fn main() { diff --git a/kayak_font/Cargo.toml b/kayak_font/Cargo.toml index f219c8f380491b504034586481ca2df3e6b7fdf6..08026172e2d7970691a159ee9303a6cb82aeab79 100644 --- a/kayak_font/Cargo.toml +++ b/kayak_font/Cargo.toml @@ -7,7 +7,6 @@ resolver = "2" authors = ["John Mitchell"] homepage = "https://github.com/StarArawn/kayak_ui" repository = "https://github.com/StarArawn/kayak_ui" -license = "MIT OR Apache-2.0" license-file = "../LICENSE" exclude = ["assets/*"] @@ -28,8 +27,8 @@ image = "0.24" # Provides UAX #14 line break segmentation xi-unicode = "0.3" -bevy = { version = "0.9", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] } +bevy = { version = "0.10", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] } [dev-dependencies] -bevy = { version = "0.9" } +bevy = { version = "0.10" } bytemuck = "1.12.0" diff --git a/kayak_font/src/bevy/mod.rs b/kayak_font/src/bevy/mod.rs index d2140100b608b146a22422944f864ef9ec263c01..97da83234b987dc4c5a330f4a1d4382327641f8a 100644 --- a/kayak_font/src/bevy/mod.rs +++ b/kayak_font/src/bevy/mod.rs @@ -11,8 +11,8 @@ mod loader; mod renderer; mod plugin { - use bevy::prelude::{AddAsset, Plugin}; - use bevy::render::{RenderApp, RenderStage}; + use bevy::prelude::{AddAsset, IntoSystemAppConfig, IntoSystemConfig, Plugin}; + use bevy::render::{ExtractSchedule, RenderApp, RenderSet}; use crate::bevy::font_texture::init_font_texture; use crate::KayakFont; @@ -32,8 +32,8 @@ mod plugin { render_app .init_resource::<FontTextureCache>() .init_resource::<ExtractedFonts>() - .add_system_to_stage(RenderStage::Extract, extract_fonts) - .add_system_to_stage(RenderStage::Prepare, prepare_fonts); + .add_system(extract_fonts.in_schedule(ExtractSchedule)) + .add_system(prepare_fonts.in_set(RenderSet::Prepare)); } } } diff --git a/kayak_font/src/bevy/renderer/font_texture_cache.rs b/kayak_font/src/bevy/renderer/font_texture_cache.rs index ec1baecfc02a7489c1f2b4f9e5e67b783ea1f21c..eb7e153e65287f93986dc683719a6354395212e4 100644 --- a/kayak_font/src/bevy/renderer/font_texture_cache.rs +++ b/kayak_font/src/bevy/renderer/font_texture_cache.rs @@ -126,6 +126,7 @@ impl FontTextureCache { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, + view_formats: &[format], format, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, }; @@ -163,6 +164,7 @@ impl FontTextureCache { texture, sampler, texture_view, + mip_level_count: 1, size: Vec2 { x: size.0 as f32, y: size.1 as f32, @@ -184,6 +186,7 @@ impl FontTextureCache { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, + view_formats: &[TextureFormat::Rgba8Unorm], format: TextureFormat::Rgba8Unorm, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, }; @@ -208,6 +211,7 @@ impl FontTextureCache { texture, sampler, texture_view, + mip_level_count: 1, size: Vec2 { x: 1.0, y: 1.0 }, texture_format: TextureFormat::Rgba8Unorm, }; diff --git a/kayak_ui_macros/Cargo.toml b/kayak_ui_macros/Cargo.toml index 9be77cd90343b44f91567a30049ddfee3119611b..155a45fec4ef267c35f6eaf1c06a44437483f484 100644 --- a/kayak_ui_macros/Cargo.toml +++ b/kayak_ui_macros/Cargo.toml @@ -7,7 +7,6 @@ resolver = "2" authors = ["John Mitchell"] homepage = "https://github.com/StarArawn/kayak_ui" repository = "https://github.com/StarArawn/kayak_ui" -license = "MIT OR Apache-2.0" license-file = "../LICENSE" [lib] diff --git a/src/camera/camera.rs b/src/camera/camera.rs index f432acedb4b8a8aa7a0ea9deb165dd981dcb0c70..c9ca4a8ec0bd51ce383bdab54889f6085f217ee2 100644 --- a/src/camera/camera.rs +++ b/src/camera/camera.rs @@ -1,9 +1,8 @@ use bevy::{ core_pipeline::clear_color::ClearColorConfig, - ecs::query::QueryItem, - prelude::{Bundle, Component, GlobalTransform, Transform, With}, + prelude::{Bundle, Component, GlobalTransform, Transform, Vec2}, render::{ - camera::{Camera, CameraProjection, CameraRenderGraph, WindowOrigin}, + camera::{Camera, CameraProjection, CameraRenderGraph}, extract_component::ExtractComponent, primitives::Frustum, view::VisibleEntities, @@ -15,20 +14,11 @@ use crate::{context::KayakRootContext, event_dispatcher::EventDispatcher}; use super::ortho::UIOrthographicProjection; /// Kayak UI's default UI camera. -#[derive(Component, Clone, Default)] +#[derive(Component, ExtractComponent, Clone, Default)] pub struct CameraUIKayak { pub clear_color: ClearColorConfig, } -impl ExtractComponent for CameraUIKayak { - type Query = &'static Self; - type Filter = With<Camera>; - - fn extract_component(item: QueryItem<Self::Query>) -> Self { - item.clone() - } -} - /// Kayak UI's default UI camera bundle. #[derive(Bundle)] pub struct UICameraBundle { @@ -60,7 +50,7 @@ impl UICameraBundle { let orthographic_projection = UIOrthographicProjection { far, - window_origin: WindowOrigin::BottomLeft, + window_origin: Vec2::new(0.0, 0.0), ..Default::default() }; @@ -68,15 +58,10 @@ impl UICameraBundle { let view_projection = orthographic_projection.get_projection_matrix() * transform.compute_matrix().inverse(); - let frustum = Frustum::from_view_projection( - &view_projection, - &transform.translation, - &transform.back(), - orthographic_projection.far(), - ); + let frustum = Frustum::from_view_projection(&view_projection); UICameraBundle { camera: Camera { - priority: isize::MAX - 1, + order: isize::MAX - 1, ..Default::default() }, camera_render_graph: CameraRenderGraph::new(bevy::core_pipeline::core_2d::graph::NAME), diff --git a/src/camera/mod.rs b/src/camera/mod.rs index 60e86c38d313c7d30e88d0dc490618246882de3e..f260d649c4ff1ded0f0a3befe5b44e5f7697b1b2 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -1,23 +1,24 @@ -use bevy::{ - prelude::{CoreStage, Plugin}, - render::{camera::CameraProjectionPlugin, extract_component::ExtractComponentPlugin}, -}; - -mod camera; -mod ortho; - -pub use camera::{CameraUIKayak, UICameraBundle}; -pub(crate) use ortho::UIOrthographicProjection; - -pub struct KayakUICameraPlugin; - -impl Plugin for KayakUICameraPlugin { - fn build(&self, app: &mut bevy::prelude::App) { - app.add_system_to_stage( - CoreStage::PostUpdate, - bevy::render::camera::camera_system::<UIOrthographicProjection>, - ) - .add_plugin(CameraProjectionPlugin::<UIOrthographicProjection>::default()) - .add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default()); - } -} +use bevy::{ + ecs::query::QueryItem, + prelude::*, + render::extract_component::{ExtractComponent, ExtractComponentPlugin}, +}; + +#[derive(Component, Default, Debug, Clone, Copy)] +pub struct CameraUIKayak; +impl ExtractComponent for CameraUIKayak { + type Query = &'static Self; + type Filter = With<Camera>; + type Out = CameraUIKayak; + + fn extract_component(item: QueryItem<Self::Query>) -> Option<Self::Out> { + Some(item.clone()) + } +} + +pub struct KayakUICameraPlugin; +impl Plugin for KayakUICameraPlugin { + fn build(&self, app: &mut bevy::prelude::App) { + app.add_plugin(ExtractComponentPlugin::<CameraUIKayak>::default()); + } +} diff --git a/src/camera/ortho.rs b/src/camera/ortho.rs index bb7a800f9c75ac5010c0f9ee193d6fc8dbd578b4..cd6d0244fd5d21d1b4eda58faf16f12e21c8b462 100644 --- a/src/camera/ortho.rs +++ b/src/camera/ortho.rs @@ -1,9 +1,9 @@ use bevy::ecs::reflect::ReflectComponent; -use bevy::prelude::Component; +use bevy::prelude::{Component, Vec2}; use bevy::{ math::Mat4, reflect::Reflect, - render::camera::{CameraProjection, ScalingMode, WindowOrigin}, + render::camera::{CameraProjection, ScalingMode}, }; /// Kayak UI's default orthographic projection matrix @@ -19,7 +19,7 @@ pub struct UIOrthographicProjection { pub top: f32, pub near: f32, pub far: f32, - pub window_origin: WindowOrigin, + pub window_origin: Vec2, pub scaling_mode: ScalingMode, pub scale: f32, } @@ -39,8 +39,11 @@ impl CameraProjection for UIOrthographicProjection { } fn update(&mut self, width: f32, height: f32) { - match (&self.scaling_mode, &self.window_origin) { - (ScalingMode::WindowSize, WindowOrigin::BottomLeft) => { + match &self.scaling_mode { + ScalingMode::WindowSize(scale) => { + if *scale != 1.0 || self.window_origin != Vec2::ZERO { + return; + } self.left = 0.0; self.right = width; self.top = 0.0; @@ -64,8 +67,8 @@ impl Default for UIOrthographicProjection { top: 1.0, near: 0.0, far: 1000.0, - window_origin: WindowOrigin::Center, - scaling_mode: ScalingMode::WindowSize, + window_origin: Vec2::new(0.5, 0.5), + scaling_mode: ScalingMode::WindowSize(1.0), scale: 1.0, } } diff --git a/src/context.rs b/src/context.rs index ac9b8b8e36bdf32896d70b976966ce47c12eb691..3b19a7f35783feee29afd7295e6e9721c6c36403 100644 --- a/src/context.rs +++ b/src/context.rs @@ -4,6 +4,7 @@ use bevy::{ ecs::{event::ManualEventReader, system::CommandQueue}, prelude::*, utils::{HashMap, HashSet}, + window::PrimaryWindow, }; use morphorm::Hierarchy; @@ -18,7 +19,7 @@ use crate::{ input::query_world, layout::{LayoutCache, Rect}, layout_dispatcher::LayoutEventDispatcher, - node::{DirtyNode, WrappedIndex}, + node::{DirtyNode, Node, WrappedIndex}, prelude::KayakWidgetContext, render_primitive::RenderPrimitive, styles::{ @@ -64,7 +65,7 @@ type WidgetSystems = HashMap< /// }).id(); /// // Stores the kayak app widget in the widget context's tree. /// widget_context.add_widget(None, app_entity); -/// commands.spawn(UICameraBundle::new(widget_context)); +/// commands.spawn((widget_context, EventDispatcher::default())); /// } /// /// fn main() { @@ -90,17 +91,18 @@ pub struct KayakRootContext { pub(crate) order_tree: Arc<RwLock<Tree>>, pub(crate) index: Arc<RwLock<HashMap<Entity, usize>>>, pub(crate) uninitilized_systems: HashSet<String>, + pub camera_entity: Entity, } impl Default for KayakRootContext { fn default() -> Self { - Self::new() + Self::new(Entity::from_raw(0)) } } impl KayakRootContext { /// Creates a new widget context. - pub fn new() -> Self { + pub fn new(camera_entity: Entity) -> Self { Self { tree: Arc::new(RwLock::new(Tree::default())), layout_cache: Arc::new(RwLock::new(LayoutCache::default())), @@ -115,6 +117,7 @@ impl KayakRootContext { index: Default::default(), order_tree: Default::default(), uninitilized_systems: Default::default(), + camera_entity, } } @@ -483,7 +486,7 @@ fn update_widgets_sys(world: &mut World) { world, ); - for (camera_entity, mut context) in context_data.drain(..) { + 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); @@ -516,7 +519,7 @@ fn update_widgets_sys(world: &mut World) { // dbg!("Updating widgets!"); update_widgets( - camera_entity, + context.camera_entity, world, &context.tree, &context.layout_cache, @@ -564,7 +567,7 @@ fn update_widgets_sys(world: &mut World) { indices.clear(); } - world.entity_mut(camera_entity).insert(context); + world.entity_mut(entity).insert(context); } } @@ -586,14 +589,14 @@ fn update_widgets( ) { for entity in widgets.iter() { // A small hack to add parents to widgets - let mut command_queue = CommandQueue::default(); - { - let mut commands = Commands::new(&mut command_queue, &world); - if let Some(mut entity_commands) = commands.get_entity(entity.0) { - entity_commands.set_parent(camera_entity); - } - } - command_queue.apply(world); + // let mut command_queue = CommandQueue::default(); + // { + // let mut commands = Commands::new(&mut command_queue, &world); + // if let Some(mut entity_commands) = commands.get_entity(entity.0) { + // entity_commands.set_parent(camera_entity); + // } + // } + // command_queue.apply(world); if let Some(entity_ref) = world.get_entity(entity.0) { if let Some(widget_type) = entity_ref.get::<WidgetName>() { @@ -808,6 +811,7 @@ fn update_widget( let new_tick = widget_update_system.get_last_change_tick(); new_ticks.insert(widget_type.clone(), new_tick); widget_update_system.set_last_change_tick(old_tick); + widget_update_system.apply_buffers(world); if should_rerender { if let Ok(cloned_widget_entities) = cloned_widget_entities.try_read() { @@ -912,7 +916,7 @@ fn update_widget( // Mark node as needing a recalculation of rendering/layout. commands.entity(entity.0).insert(DirtyNode); - for (_index, changed_entity, _parent, changes) in diff.changes.iter() { + for (_index, changed_entity, parent, changes) in diff.changes.iter() { if changes.iter().any(|change| *change == Change::Deleted) { // commands.entity(changed_entity.0).despawn(); // commands.entity(changed_entity.0).remove::<DirtyNode>(); @@ -921,6 +925,10 @@ fn update_widget( if changes.iter().any(|change| *change == Change::Inserted) { if let Some(mut entity_commands) = commands.get_entity(changed_entity.0) { entity_commands.insert(Mounted); + entity_commands.set_parent(parent.0); + } + if let Some(mut parent_commands) = commands.get_entity(parent.0) { + parent_commands.add_child(changed_entity.0); } } } @@ -990,9 +998,13 @@ impl Plugin for KayakContextPlugin { .add_plugin(crate::camera::KayakUICameraPlugin) .add_plugin(crate::render::BevyKayakUIRenderPlugin) .register_type::<Node>() - .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()) + .add_system(crate::input::process_events.in_base_set(CoreSet::Update)) + .add_system(update_widgets_sys.in_base_set(CoreSet::PostUpdate)) + .add_system( + calculate_ui + .after(update_widgets_sys) + .in_base_set(CoreSet::PostUpdate), + ) .add_system(crate::window_size::update_window_size); // Register reflection types. @@ -1061,10 +1073,11 @@ fn calculate_ui(world: &mut World) { } } - 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 Ok(mut window) = world + .query_filtered::<&mut Window, With<PrimaryWindow>>() + .get_single_mut(world) + { + window.cursor.icon = context.current_cursor; } } diff --git a/src/event.rs b/src/event.rs index 7d24367d343a6ff2a5055ac834478d12dfec7101..f8bf74ee3819d856e6542951d57f40419ddaa3e1 100644 --- a/src/event.rs +++ b/src/event.rs @@ -8,7 +8,7 @@ use crate::{ /// An event type sent to widgets #[derive(Clone)] -pub struct Event { +pub struct KEvent { /// The node targeted by this event pub target: Entity, /// The current target of this event @@ -23,7 +23,7 @@ pub struct Event { pub(crate) on_change_systems: Vec<OnChange>, } -impl PartialEq for Event { +impl PartialEq for KEvent { fn eq(&self, other: &Self) -> bool { self.target == other.target && self.current_target == other.current_target @@ -33,7 +33,7 @@ impl PartialEq for Event { } } -impl Default for Event { +impl Default for KEvent { fn default() -> Self { Self { target: Entity::from_raw(0), @@ -46,7 +46,7 @@ impl Default for Event { } } -impl Event { +impl KEvent { /// Create a new event /// /// This is the preferred method for creating an event as it automatically sets up diff --git a/src/event_dispatcher.rs b/src/event_dispatcher.rs index e0750a80b84928459de9798232ec6761ce6ff972..fe7dc526457d8b711799eab13c30e2fd14ce399a 100644 --- a/src/event_dispatcher.rs +++ b/src/event_dispatcher.rs @@ -6,7 +6,7 @@ use bevy::{ use crate::{ context::KayakRootContext, cursor::{CursorEvent, PointerEvents, ScrollEvent, ScrollUnit}, - event::{Event, EventType}, + event::{EventType, KEvent}, focus_tree::FocusTree, input_event::{InputEvent, InputEventCategory}, keyboard_event::{KeyboardEvent, KeyboardModifiers}, @@ -185,21 +185,21 @@ impl EventDispatcher { self.dispatch_events(events, context, world); } - /// Dispatch an [Event](crate::Event) + /// Dispatch an [KEvent](crate::KEvent) #[allow(dead_code)] pub fn dispatch_event( &mut self, - event: Event, + event: KEvent, context: &mut KayakRootContext, world: &mut World, ) { self.dispatch_events(vec![event], context, world); } - /// Dispatch a set of [Events](crate::Event) + /// Dispatch a set of [KEvents](crate::KEvent) pub fn dispatch_events( &mut self, - events: Vec<Event>, + events: Vec<KEvent>, context: &mut KayakRootContext, world: &mut World, ) { @@ -211,7 +211,7 @@ impl EventDispatcher { // Create a copy of the event, specific for this node // This is to make sure unauthorized changes to the event are not propagated // (e.g., changing the event type, removing the target, etc.) - let mut node_event = Event { + let mut node_event = KEvent { current_target: index.0, ..event.clone() }; @@ -221,7 +221,7 @@ impl EventDispatcher { // --- Call Event --- // if let Some(mut entity) = world.get_entity_mut(index.0) { - if let Some(mut on_event) = entity.remove::<OnEvent>() { + if let Some(mut on_event) = entity.take::<OnEvent>() { let mut event_dispatcher_context = EventDispatcherContext { cursor_capture: self.cursor_capture, }; @@ -304,14 +304,14 @@ impl EventDispatcher { self.previous_events = next_events; } - /// Generates a stream of [Events](crate::Event) from a set of [InputEvents](crate::InputEvent) + /// Generates a stream of [KEvents](crate::KEvent) from a set of [InputEvents](crate::InputEvent) fn build_event_stream( &mut self, input_events: &[InputEvent], context: &mut KayakRootContext, world: &mut World, - ) -> Vec<Event> { - let mut event_stream = Vec::<Event>::new(); + ) -> Vec<KEvent> { + let mut event_stream = Vec::<KEvent>::new(); let mut states: HashMap<EventType, EventState> = HashMap::new(); if let Ok(node_tree) = context.tree.try_read() { @@ -451,7 +451,7 @@ impl EventDispatcher { // These events are ones that require a specific target and need the tree to be evaluated before selecting the best match for (event_type, state) in states { if let Some(node) = state.best_match { - event_stream.push(Event::new(node.0, event_type)); + event_stream.push(KEvent::new(node.0, event_type)); match event_type { EventType::Focus => { @@ -459,7 +459,7 @@ impl EventDispatcher { if let Some(current_focus) = focus_tree.current() { if current_focus != node { event_stream - .push(Event::new(current_focus.0, EventType::Blur)); + .push(KEvent::new(current_focus.0, EventType::Blur)); } } focus_tree.focus(node); @@ -476,7 +476,7 @@ impl EventDispatcher { if !had_focus_event && input_events.contains(&InputEvent::MouseLeftPress) { // A mouse press didn't contain a focus event -> blur if let Some(current_focus) = focus_tree.current() { - event_stream.push(Event::new(current_focus.0, EventType::Blur)); + event_stream.push(KEvent::new(current_focus.0, EventType::Blur)); focus_tree.blur(); } } @@ -513,7 +513,7 @@ impl EventDispatcher { /// * `widget_manager`: The widget manager /// * `ignore_layout`: Whether to ignore layout (useful for handling captured events) /// - /// returns: Vec<Event> + /// returns: Vec<KEvent> fn process_pointer_events( &mut self, input_event: &InputEvent, @@ -522,8 +522,8 @@ impl EventDispatcher { world: &mut World, context: &KayakRootContext, ignore_layout: bool, - ) -> Vec<Event> { - let mut event_stream = Vec::<Event>::new(); + ) -> Vec<KEvent> { + let mut event_stream = Vec::<KEvent>::new(); let (node, depth) = tree_node; // let widget_name = world.entity(node.0).get::<WidgetName>(); @@ -539,7 +539,7 @@ impl EventDispatcher { if was_contained { // Mouse out should fire even when event_stream - .push(Event::new(node.0, EventType::MouseOut(cursor_event))); + .push(KEvent::new(node.0, EventType::MouseOut(cursor_event))); // Self::update_state( // states, // (node, depth), @@ -688,12 +688,12 @@ impl EventDispatcher { input_event: &InputEvent, _states: &mut HashMap<EventType, EventState>, focus_tree: &FocusTree, - ) -> Vec<Event> { + ) -> Vec<KEvent> { let mut event_stream = Vec::new(); if let Some(current_focus) = focus_tree.current() { match input_event { InputEvent::CharEvent { c } => { - event_stream.push(Event::new(current_focus.0, EventType::CharInput { c: *c })) + event_stream.push(KEvent::new(current_focus.0, EventType::CharInput { c: *c })) } InputEvent::Keyboard { key, is_pressed } => { // === Modifers === // @@ -715,12 +715,12 @@ impl EventDispatcher { // === Event === // if *is_pressed { - event_stream.push(Event::new( + event_stream.push(KEvent::new( current_focus.0, EventType::KeyDown(KeyboardEvent::new(*key, self.keyboard_modifiers)), )) } else { - event_stream.push(Event::new( + event_stream.push(KEvent::new( current_focus.0, EventType::KeyUp(KeyboardEvent::new(*key, self.keyboard_modifiers)), )) @@ -789,7 +789,12 @@ impl EventDispatcher { } /// Executes default actions for events - fn execute_default(&mut self, event: Event, context: &mut KayakRootContext, world: &mut World) { + fn execute_default( + &mut self, + event: KEvent, + context: &mut KayakRootContext, + world: &mut World, + ) { match event.event_type { EventType::KeyDown(evt) => match evt.key() { KeyCode::Tab => { @@ -808,10 +813,10 @@ impl EventDispatcher { }; if let Some(index) = index { - let mut events = vec![Event::new(index.0, EventType::Focus)]; + let mut events = vec![KEvent::new(index.0, EventType::Focus)]; if let Some(current_focus) = current_focus { if current_focus != index { - events.push(Event::new(current_focus.0, EventType::Blur)); + events.push(KEvent::new(current_focus.0, EventType::Blur)); } } if let Ok(mut focus_tree) = context.focus_tree.try_write() { diff --git a/src/input.rs b/src/input.rs index c798500d9346e9d47337ac71644a160370fd8bee..d7adc3e00e59272644d9fa1fc25f2706ecff668f 100644 --- a/src/input.rs +++ b/src/input.rs @@ -5,6 +5,7 @@ use bevy::{ ButtonState, }, prelude::*, + window::PrimaryWindow, }; use crate::{ @@ -14,13 +15,11 @@ use crate::{ }; pub(crate) fn process_events(world: &mut World) { - let window_size = if let Some(windows) = world.get_resource::<Windows>() { - if let Some(window) = windows.get_primary() { - Vec2::new(window.width(), window.height()) - } else { - log::warn!("Couldn't find primiary window!"); - return; - } + let window_size = if let Ok(window) = world + .query_filtered::<&Window, With<PrimaryWindow>>() + .get_single(&world) + { + Vec2::new(window.width(), window.height()) } else { log::warn!("Couldn't find primiary window!"); return; @@ -133,7 +132,7 @@ pub(crate) fn query_world<T: bevy::ecs::system::SystemParam + 'static, F, R>( world: &mut World, ) -> R where - F: FnOnce(<T::Fetch as bevy::ecs::system::SystemParamFetch<'_, '_>>::Item) -> R, + F: FnOnce(<T as bevy::ecs::system::SystemParam>::Item<'_, '_>) -> R, { let mut system_state = bevy::ecs::system::SystemState::<T>::new(world); let r = { diff --git a/src/layout_dispatcher.rs b/src/layout_dispatcher.rs index 913c7eab9f0cdccef2e089297996c68621cb07fd..fc4571b6a4aca1652d86f748c8339836150f5392 100644 --- a/src/layout_dispatcher.rs +++ b/src/layout_dispatcher.rs @@ -67,7 +67,7 @@ impl LayoutEventDispatcher { // We should be able to just get layout from WidgetManager here // since the layouts will be calculated by this point if let Some(mut entity) = world.get_entity_mut(index.0) { - if let Some(mut on_layout) = entity.remove::<OnLayout>() { + if let Some(mut on_layout) = entity.take::<OnLayout>() { if let Some(rect) = layout_cache.rect.get(&index) { // dbg!(format!("Processing event for: {:?}", entity.id())); let layout_event = LayoutEvent::new(*rect, flags, index.0); diff --git a/src/lib.rs b/src/lib.rs index d6e61f6eab0c82e6bbe55c357cc04a1579d96faf..02ac24264915c4f44ccfe4cbc0ab1f7a9f8ff887 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -38,7 +38,7 @@ pub use camera::*; pub const DEFAULT_FONT: &str = "Kayak-Default"; pub mod prelude { - pub use crate::camera::UICameraBundle; + pub use crate::camera::*; pub use crate::children::KChildren; pub use crate::clone_component::PreviousWidget; pub use crate::context::*; @@ -53,6 +53,7 @@ pub mod prelude { pub use crate::on_change::OnChange; pub use crate::on_event::OnEvent; pub use crate::on_layout::OnLayout; + pub use crate::render::draw_ui_graph; pub use crate::render::font::FontMapping; pub use crate::styles::*; pub use crate::tree::*; diff --git a/src/on_event.rs b/src/on_event.rs index efe92c5dc355acbde4bda24d9ec960f1c2f92e4b..0f6ca069f482791abb0d73c1fbc796afcd05a31a 100644 --- a/src/on_event.rs +++ b/src/on_event.rs @@ -2,7 +2,7 @@ use bevy::prelude::{Component, Entity, In, IntoSystem, System, World}; use std::fmt::{Debug, Formatter}; use std::sync::{Arc, RwLock}; -use crate::event::Event; +use crate::event::KEvent; use crate::event_dispatcher::EventDispatcherContext; use crate::widget_state::WidgetState; @@ -17,8 +17,8 @@ pub struct OnEvent { system: Arc< RwLock< dyn System< - In = (EventDispatcherContext, WidgetState, Event, Entity), - Out = (EventDispatcherContext, Event), + In = (EventDispatcherContext, WidgetState, KEvent, Entity), + Out = (EventDispatcherContext, KEvent), >, >, >, @@ -42,8 +42,8 @@ impl OnEvent { /// 2. The event pub fn new<Params>( system: impl IntoSystem< - (EventDispatcherContext, WidgetState, Event, Entity), - (EventDispatcherContext, Event), + (EventDispatcherContext, WidgetState, KEvent, Entity), + (EventDispatcherContext, KEvent), Params, >, ) -> OnEvent { @@ -61,9 +61,9 @@ impl OnEvent { mut event_dispatcher_context: EventDispatcherContext, widget_state: WidgetState, entity: Entity, - mut event: Event, + mut event: KEvent, world: &mut World, - ) -> (EventDispatcherContext, Event) { + ) -> (EventDispatcherContext, KEvent) { if let Ok(mut system) = self.system.try_write() { if !self.has_initialized { system.initialize(world); diff --git a/src/render/extract.rs b/src/render/extract.rs index e0a4ded369621cd31000a147291965289020d94e..ce034cedd4ddddc980666397f51b8d2864c9f376 100644 --- a/src/render/extract.rs +++ b/src/render/extract.rs @@ -3,17 +3,27 @@ use crate::{ node::Node, render_primitive::RenderPrimitive, styles::Corner, + CameraUIKayak, }; use bevy::{ - prelude::{Assets, Camera, Color, Commands, Entity, Image, Plugin, Query, Rect, Res, Vec2}, - render::{Extract, RenderApp, RenderStage}, - window::Windows, + prelude::{ + Assets, Camera, Camera2d, Camera3d, Color, Commands, Component, Entity, GlobalTransform, + Image, IntoSystemAppConfig, IntoSystemAppConfigs, Mat4, Plugin, Query, Rect, Res, UVec4, + Vec2, With, + }, + render::{ + render_phase::RenderPhase, + view::{ColorGrading, ExtractedView}, + Extract, ExtractSchedule, RenderApp, + }, + window::{PrimaryWindow, Window, WindowRef}, }; use kayak_font::KayakFont; use super::{ font::{self, FontMapping}, image, nine_patch, texture_atlas, + ui_pass::TransparentUI, unified::pipeline::{ExtractQuadBundle, ExtractedQuad, UIQuadType}, }; @@ -25,34 +35,53 @@ pub struct BevyKayakUIExtractPlugin; impl Plugin for BevyKayakUIExtractPlugin { fn build(&self, app: &mut bevy::prelude::App) { let render_app = app.sub_app_mut(RenderApp); - render_app.add_system_to_stage(RenderStage::Extract, extract); + render_app.add_system(extract.in_schedule(ExtractSchedule)); + render_app.add_systems( + ( + extract_default_ui_camera_view::<Camera2d>, + extract_default_ui_camera_view::<Camera3d>, + ) + .in_schedule(ExtractSchedule), + ); } } pub fn extract( mut commands: Commands, - context_query: Extract<Query<(Entity, &KayakRootContext, &Camera)>>, + context_query: Extract<Query<(Entity, &KayakRootContext)>>, fonts: Extract<Res<Assets<KayakFont>>>, font_mapping: Extract<Res<FontMapping>>, node_query: Extract<Query<&Node>>, widget_names: Extract<Query<&WidgetName>>, images: Extract<Res<Assets<Image>>>, - windows: Extract<Res<Windows>>, + primary_window: Extract<Query<&Window, With<PrimaryWindow>>>, + cameras: Extract<Query<&Camera>>, ) { let mut render_primitives = Vec::new(); - for (entity, 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 - } + for (_entity, context) in context_query.iter() { + let dpi = if let Ok(camera) = cameras.get(context.camera_entity) { + match &camera.target { + bevy::render::camera::RenderTarget::Window(window_ref) => match window_ref { + WindowRef::Primary => { + if let Ok(window) = primary_window.get_single() { + window.scale_factor() as f32 + } else { + 1.0 + } + } + _ => 1.0, + }, + _ => 1.0, } - _ => 1.0, + } else { + 1.0 }; let mut new_render_primitives = context.build_render_primitives(&node_query, &widget_names); - render_primitives.extend(new_render_primitives.drain(..).map(|r| (entity, dpi, r))); + render_primitives.extend( + new_render_primitives + .drain(..) + .map(|r| (context.camera_entity, dpi, r)), + ); } let mut extracted_quads = Vec::new(); @@ -120,3 +149,48 @@ pub fn extract( // dbg!(&extracted_quads); commands.spawn_batch(extracted_quads); } + +#[derive(Component)] +pub struct DefaultCameraView(pub Entity); + +const UI_CAMERA_TRANSFORM_OFFSET: f32 = -0.1; + +pub fn extract_default_ui_camera_view<T: Component>( + mut commands: Commands, + query: Extract<Query<(Entity, &Camera, &CameraUIKayak), With<T>>>, +) { + for (entity, camera, _camera_ui) in &query { + if let (Some(logical_size), Some((physical_origin, _)), Some(physical_size)) = ( + camera.logical_viewport_size(), + camera.physical_viewport_rect(), + camera.physical_viewport_size(), + ) { + // use a projection matrix with the origin in the top left instead of the bottom left that comes with OrthographicProjection + let projection_matrix = + Mat4::orthographic_rh(0.0, logical_size.x, logical_size.y, 0.0, 0.0, 1000.0); + let default_camera_view = commands + .spawn(ExtractedView { + projection: projection_matrix, + transform: GlobalTransform::from_xyz( + 0.0, + 0.0, + 1000.0 + UI_CAMERA_TRANSFORM_OFFSET, + ), + hdr: camera.hdr, + viewport: UVec4::new( + physical_origin.x, + physical_origin.y, + physical_size.x, + physical_size.y, + ), + view_projection: None, + color_grading: ColorGrading::default(), + }) + .id(); + commands.get_or_spawn(entity).insert(( + DefaultCameraView(default_camera_view), + RenderPhase::<TransparentUI>::default(), + )); + } + } +} diff --git a/src/render/mod.rs b/src/render/mod.rs index 67dbf1cfd342bc94164963625f34aa1027682ca9..c6b1e80e1865ee9d3c2898706632d9940ff129cb 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -1,9 +1,9 @@ use bevy::{ - prelude::{App, Camera, Commands, Entity, Plugin, Query, With}, + prelude::{App, Camera, Commands, Entity, IntoSystemAppConfig, Plugin, Query, With}, render::{ render_graph::{RenderGraph, RunGraphOnViewNode, SlotInfo, SlotType}, render_phase::{DrawFunctions, RenderPhase}, - Extract, RenderApp, RenderStage, + Extract, ExtractSchedule, RenderApp, }, }; @@ -43,7 +43,7 @@ impl Plugin for BevyKayakUIRenderPlugin { let render_app = app.sub_app_mut(RenderApp); render_app .init_resource::<DrawFunctions<TransparentUI>>() - .add_system_to_stage(RenderStage::Extract, extract_core_pipeline_camera_phases); + .add_system(extract_core_pipeline_camera_phases.in_schedule(ExtractSchedule)); // .add_system_to_stage(RenderStage::PhaseSort, sort_phase_system::<TransparentUI>); // let pass_node_ui = MainPassUINode::new(&mut render_app.world); @@ -78,32 +78,24 @@ impl Plugin for BevyKayakUIRenderPlugin { draw_ui_graph::node::MAIN_PASS, RunGraphOnViewNode::new(draw_ui_graph::NAME), ); - graph_2d - .add_node_edge( - bevy::core_pipeline::core_2d::graph::node::MAIN_PASS, - draw_ui_graph::node::MAIN_PASS, - ) - .unwrap(); - graph_2d - .add_slot_edge( - graph_2d.input_node().unwrap().id, - bevy::core_pipeline::core_2d::graph::input::VIEW_ENTITY, - draw_ui_graph::node::MAIN_PASS, - RunGraphOnViewNode::IN_VIEW, - ) - .unwrap(); - graph_2d - .add_node_edge( - bevy::core_pipeline::core_2d::graph::node::TONEMAPPING, - draw_ui_graph::node::MAIN_PASS, - ) - .unwrap(); - graph_2d - .add_node_edge( - draw_ui_graph::node::MAIN_PASS, - bevy::core_pipeline::core_2d::graph::node::UPSCALING, - ) - .unwrap(); + graph_2d.add_node_edge( + bevy::core_pipeline::core_2d::graph::node::MAIN_PASS, + draw_ui_graph::node::MAIN_PASS, + ); + graph_2d.add_slot_edge( + graph_2d.input_node().id, + bevy::core_pipeline::core_2d::graph::input::VIEW_ENTITY, + draw_ui_graph::node::MAIN_PASS, + RunGraphOnViewNode::IN_VIEW, + ); + graph_2d.add_node_edge( + bevy::core_pipeline::core_2d::graph::node::TONEMAPPING, + draw_ui_graph::node::MAIN_PASS, + ); + graph_2d.add_node_edge( + draw_ui_graph::node::MAIN_PASS, + bevy::core_pipeline::core_2d::graph::node::UPSCALING, + ); } if let Some(graph_3d) = graph.get_sub_graph_mut(bevy::core_pipeline::core_3d::graph::NAME) { @@ -112,32 +104,24 @@ impl Plugin for BevyKayakUIRenderPlugin { draw_ui_graph::node::MAIN_PASS, RunGraphOnViewNode::new(draw_ui_graph::NAME), ); - graph_3d - .add_node_edge( - bevy::core_pipeline::core_3d::graph::node::MAIN_PASS, - draw_ui_graph::node::MAIN_PASS, - ) - .unwrap(); - graph_3d - .add_node_edge( - bevy::core_pipeline::core_3d::graph::node::TONEMAPPING, - draw_ui_graph::node::MAIN_PASS, - ) - .unwrap(); - graph_3d - .add_node_edge( - draw_ui_graph::node::MAIN_PASS, - bevy::core_pipeline::core_3d::graph::node::UPSCALING, - ) - .unwrap(); - graph_3d - .add_slot_edge( - graph_3d.input_node().unwrap().id, - bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY, - draw_ui_graph::node::MAIN_PASS, - RunGraphOnViewNode::IN_VIEW, - ) - .unwrap(); + graph_3d.add_node_edge( + bevy::core_pipeline::core_3d::graph::node::MAIN_PASS, + draw_ui_graph::node::MAIN_PASS, + ); + graph_3d.add_node_edge( + bevy::core_pipeline::core_3d::graph::node::TONEMAPPING, + draw_ui_graph::node::MAIN_PASS, + ); + graph_3d.add_node_edge( + draw_ui_graph::node::MAIN_PASS, + bevy::core_pipeline::core_3d::graph::node::UPSCALING, + ); + graph_3d.add_slot_edge( + graph_3d.input_node().id, + bevy::core_pipeline::core_3d::graph::input::VIEW_ENTITY, + draw_ui_graph::node::MAIN_PASS, + RunGraphOnViewNode::IN_VIEW, + ); } app.add_plugin(font::TextRendererPlugin) @@ -154,14 +138,12 @@ fn get_ui_graph(render_app: &mut App) -> RenderGraph { draw_ui_graph::input::VIEW_ENTITY, SlotType::Entity, )]); - ui_graph - .add_slot_edge( - input_node_id, - draw_ui_graph::input::VIEW_ENTITY, - draw_ui_graph::node::MAIN_PASS, - MainPassUINode::IN_VIEW, - ) - .unwrap(); + ui_graph.add_slot_edge( + input_node_id, + draw_ui_graph::input::VIEW_ENTITY, + draw_ui_graph::node::MAIN_PASS, + MainPassUINode::IN_VIEW, + ); ui_graph } diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs index 70ed3337c8b8b7e7967a16bdbfcd4926c5d7d24b..d7c8edf588d5ff6e44ca721783e44b7ce57f9b60 100644 --- a/src/render/ui_pass.rs +++ b/src/render/ui_pass.rs @@ -1,116 +1,123 @@ -use bevy::core_pipeline::clear_color::ClearColorConfig; -use bevy::ecs::prelude::*; -use bevy::prelude::ClearColor; -use bevy::render::render_phase::{DrawFunctionId, PhaseItem}; -use bevy::render::render_resource::CachedRenderPipelineId; -use bevy::render::{ - render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, - render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass}, - render_resource::{LoadOp, Operations, RenderPassDescriptor}, - renderer::RenderContext, - view::{ExtractedView, ViewTarget}, -}; -use bevy::utils::FloatOrd; - -use crate::CameraUIKayak; - -pub struct TransparentUI { - pub sort_key: FloatOrd, - pub entity: Entity, - pub pipeline: CachedRenderPipelineId, - pub draw_function: DrawFunctionId, -} - -impl PhaseItem for TransparentUI { - type SortKey = FloatOrd; - - #[inline] - fn sort_key(&self) -> Self::SortKey { - self.sort_key - } - - #[inline] - fn draw_function(&self) -> DrawFunctionId { - self.draw_function - } -} - -pub struct MainPassUINode { - query: QueryState< - ( - &'static RenderPhase<TransparentUI>, - &'static ViewTarget, - &'static CameraUIKayak, - ), - With<ExtractedView>, - >, -} - -impl MainPassUINode { - pub const IN_VIEW: &'static str = "view"; - - pub fn new(world: &mut World) -> Self { - Self { - query: world.query_filtered(), - } - } -} - -impl Node for MainPassUINode { - fn input(&self) -> Vec<SlotInfo> { - vec![SlotInfo::new(MainPassUINode::IN_VIEW, SlotType::Entity)] - } - - fn update(&mut self, world: &mut World) { - self.query.update_archetypes(world); - } - - fn run( - &self, - graph: &mut RenderGraphContext, - render_context: &mut RenderContext, - world: &World, - ) -> Result<(), NodeRunError> { - let view_entity = graph.get_input_entity(Self::IN_VIEW)?; - // adapted from bevy itself; - // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6> - let (transparent_phase, target, camera_ui) = match self.query.get_manual(world, view_entity) - { - Ok(it) => it, - _ => return Ok(()), - }; - // let clear_color = world.get_resource::<ClearColor>().unwrap(); - { - let pass_descriptor = RenderPassDescriptor { - label: Some("main_transparent_pass_UI"), - color_attachments: &[Some(target.get_unsampled_color_attachment(Operations { - load: match camera_ui.clear_color { - ClearColorConfig::Default => { - LoadOp::Clear(world.resource::<ClearColor>().0.into()) - } - ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()), - ClearColorConfig::None => LoadOp::Load, - }, - store: true, - }))], - depth_stencil_attachment: None, - }; - - let draw_functions = world - .get_resource::<DrawFunctions<TransparentUI>>() - .unwrap(); - - let render_pass = render_context - .command_encoder - .begin_render_pass(&pass_descriptor); - let mut draw_functions = draw_functions.write(); - let mut tracked_pass = TrackedRenderPass::new(render_pass); - for item in transparent_phase.items.iter() { - let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); - draw_function.draw(world, &mut tracked_pass, view_entity, item); - } - } - - Ok(()) - } -} +use bevy::ecs::prelude::*; +use bevy::render::render_phase::{DrawFunctionId, PhaseItem}; +use bevy::render::render_resource::CachedRenderPipelineId; +use bevy::render::{ + render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, + render_phase::{DrawFunctions, RenderPhase}, + render_resource::{LoadOp, Operations, RenderPassDescriptor}, + renderer::RenderContext, + view::{ExtractedView, ViewTarget}, +}; +use bevy::utils::FloatOrd; + +use crate::CameraUIKayak; + +use super::extract::DefaultCameraView; + +pub struct TransparentUI { + pub sort_key: FloatOrd, + pub entity: Entity, + pub pipeline: CachedRenderPipelineId, + pub draw_function: DrawFunctionId, +} + +impl PhaseItem for TransparentUI { + type SortKey = FloatOrd; + + #[inline] + fn sort_key(&self) -> Self::SortKey { + self.sort_key + } + + #[inline] + fn draw_function(&self) -> DrawFunctionId { + self.draw_function + } + + fn entity(&self) -> Entity { + self.entity + } +} + +pub struct MainPassUINode { + query: QueryState< + ( + &'static RenderPhase<TransparentUI>, + &'static ViewTarget, + &'static CameraUIKayak, + ), + With<ExtractedView>, + >, + default_camera_view_query: QueryState<&'static DefaultCameraView>, +} + +impl MainPassUINode { + pub const IN_VIEW: &'static str = "view"; + + pub fn new(world: &mut World) -> Self { + Self { + query: world.query_filtered(), + default_camera_view_query: world.query(), + } + } +} + +impl Node for MainPassUINode { + fn input(&self) -> Vec<SlotInfo> { + vec![SlotInfo::new(MainPassUINode::IN_VIEW, SlotType::Entity)] + } + + fn update(&mut self, world: &mut World) { + self.query.update_archetypes(world); + self.default_camera_view_query.update_archetypes(world); + } + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + world: &World, + ) -> Result<(), NodeRunError> { + let input_view_entity = graph.get_input_entity(Self::IN_VIEW)?; + // adapted from bevy itself; + // see: <https://github.com/bevyengine/bevy/commit/09a3d8abe062984479bf0e99fcc1508bb722baf6> + let (transparent_phase, target, _camera_ui) = + match self.query.get_manual(world, input_view_entity) { + Ok(it) => it, + _ => return Ok(()), + }; + + let view_entity = if let Ok(default_view) = self + .default_camera_view_query + .get_manual(world, input_view_entity) + { + default_view.0 + } else { + input_view_entity + }; + // let clear_color = world.get_resource::<ClearColor>().unwrap(); + { + let pass_descriptor = RenderPassDescriptor { + label: Some("main_transparent_pass_UI"), + color_attachments: &[Some(target.get_unsampled_color_attachment(Operations { + load: LoadOp::Load, + store: true, + }))], + depth_stencil_attachment: None, + }; + + let draw_functions = world + .get_resource::<DrawFunctions<TransparentUI>>() + .unwrap(); + + let mut tracked_pass = render_context.begin_tracked_render_pass(pass_descriptor); + let mut draw_functions = draw_functions.write(); + for item in transparent_phase.items.iter() { + let draw_function = draw_functions.get_mut(item.draw_function).unwrap(); + draw_function.draw(world, &mut tracked_pass, view_entity, item); + } + } + + Ok(()) + } +} diff --git a/src/render/unified/mod.rs b/src/render/unified/mod.rs index 7efdec3f2b7e5ddfe12abe1457659a73539a3846..53a801a4e8d0209bf5a65e9664c288d03d606a11 100644 --- a/src/render/unified/mod.rs +++ b/src/render/unified/mod.rs @@ -1,12 +1,15 @@ use bevy::{ - prelude::{Assets, Commands, HandleUntyped, Plugin, Res, Resource}, + prelude::{ + Assets, Commands, HandleUntyped, IntoSystemAppConfig, IntoSystemConfig, Plugin, Query, Res, + Resource, With, + }, reflect::TypeUuid, render::{ render_phase::DrawFunctions, render_resource::{Shader, SpecializedRenderPipelines}, - Extract, RenderApp, RenderStage, + Extract, ExtractSchedule, RenderApp, RenderSet, }, - window::Windows, + window::{PrimaryWindow, Window}, }; use crate::{ @@ -41,9 +44,9 @@ impl Plugin for UnifiedRenderPlugin { .init_resource::<UnifiedPipeline>() .init_resource::<SpecializedRenderPipelines<UnifiedPipeline>>() .init_resource::<QuadMeta>() - .add_system_to_stage(RenderStage::Extract, extract_baseline) - .add_system_to_stage(RenderStage::Prepare, pipeline::prepare_quads) - .add_system_to_stage(RenderStage::Queue, pipeline::queue_quads); + .add_system(extract_baseline.in_schedule(ExtractSchedule)) + .add_system(pipeline::prepare_quads.in_set(RenderSet::Prepare)) + .add_system(pipeline::queue_quads.in_set(RenderSet::Queue)); let draw_quad = DrawUI::new(&mut render_app.world); @@ -61,10 +64,10 @@ pub struct Dpi(f32); pub fn extract_baseline( mut commands: Commands, - windows: Extract<Res<Windows>>, + windows: Extract<Query<&Window, With<PrimaryWindow>>>, window_size: Extract<Res<WindowSize>>, ) { - let dpi = if let Some(window) = windows.get_primary() { + let dpi = if let Ok(window) = windows.get_single() { window.scale_factor() as f32 } else { 1.0 diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs index 4d5aa330e4da2f9191d55a252257e925a26ee7c8..96bf6f80e5047c780d1a8e8937c5c3c61f0d12a7 100644 --- a/src/render/unified/pipeline.rs +++ b/src/render/unified/pipeline.rs @@ -1,7 +1,8 @@ -use bevy::prelude::{Msaa, Rect, Resource}; +use bevy::prelude::{Rect, Resource}; use bevy::render::render_resource::{ DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines, }; +use bevy::render::view::{ExtractedView, ViewTarget}; use bevy::utils::FloatOrd; use bevy::{ ecs::system::{ @@ -67,27 +68,10 @@ impl FontRenderingPipeline for UnifiedPipeline { } } -bitflags::bitflags! { - #[repr(transparent)] - pub struct UnifiedPipelineKey: u32 { - const NONE = 0; - const MSAA_RESERVED_BITS = Self::MSAA_MASK_BITS << Self::MSAA_SHIFT_BITS; - } -} - -impl UnifiedPipelineKey { - const MSAA_MASK_BITS: u32 = 0b111; - const MSAA_SHIFT_BITS: u32 = 32 - Self::MSAA_MASK_BITS.count_ones(); - - pub fn from_msaa_samples(msaa_samples: u32) -> Self { - let msaa_bits = - (msaa_samples.trailing_zeros() & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS; - Self::from_bits(msaa_bits).unwrap() - } - - pub fn msaa_samples(&self) -> u32 { - 1 << ((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS) - } +#[derive(Debug, Component, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnifiedPipelineKey { + pub msaa: u32, + pub hdr: bool, } impl FromWorld for UnifiedPipeline { @@ -185,6 +169,7 @@ impl FromWorld for UnifiedPipeline { mip_level_count: 1, sample_count: 1, dimension: TextureDimension::D2, + view_formats: &[TextureFormat::Rgba8UnormSrgb], format: TextureFormat::Rgba8UnormSrgb, usage: TextureUsages::TEXTURE_BINDING | TextureUsages::COPY_DST, }; @@ -209,6 +194,7 @@ impl FromWorld for UnifiedPipeline { texture, sampler, texture_view, + mip_level_count: 1, size: Vec2::new(1.0, 1.0), texture_format: TextureFormat::Rgba8UnormSrgb, }; @@ -242,7 +228,7 @@ impl FromWorld for UnifiedPipeline { impl SpecializedRenderPipeline for UnifiedPipeline { type Key = UnifiedPipelineKey; - fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor { + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { let vertex_buffer_layout = VertexBufferLayout { array_stride: 60, step_mode: VertexStepMode::Vertex, @@ -282,7 +268,11 @@ impl SpecializedRenderPipeline for UnifiedPipeline { shader_defs: vec![], entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: if key.hdr { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }, blend: Some(BlendState { color: BlendComponent { src_factor: BlendFactor::SrcAlpha, @@ -298,12 +288,12 @@ impl SpecializedRenderPipeline for UnifiedPipeline { write_mask: ColorWrites::ALL, })], }), - layout: Some(vec![ + layout: vec![ self.view_layout.clone(), self.font_image_layout.clone(), self.types_layout.clone(), self.image_layout.clone(), - ]), + ], primitive: PrimitiveState { front_face: FrontFace::Ccw, cull_mode: None, @@ -315,11 +305,12 @@ impl SpecializedRenderPipeline for UnifiedPipeline { }, depth_stencil: None, multisample: MultisampleState { - count: 1, + count: key.msaa, mask: !0, alpha_to_coverage_enabled: false, }, label: Some("unified_pipeline".into()), + push_constant_ranges: vec![], } } } @@ -356,7 +347,7 @@ pub struct ExtractedQuad { } #[repr(C)] -#[derive(Copy, Clone, Pod, Zeroable)] +#[derive(Copy, Clone)] struct QuadVertex { pub position: [f32; 3], pub color: [f32; 4], @@ -364,6 +355,9 @@ struct QuadVertex { pub pos_size: [f32; 4], } +unsafe impl Zeroable for QuadVertex {} +unsafe impl Pod for QuadVertex {} + #[repr(C)] #[derive(Copy, Clone, ShaderType)] struct QuadType { @@ -534,11 +528,10 @@ pub fn queue_quads( mut pipelines: ResMut<SpecializedRenderPipelines<UnifiedPipeline>>, mut pipeline_cache: ResMut<PipelineCache>, mut extracted_sprites: Query<(Entity, &ExtractedQuad)>, - mut views: Query<(Entity, &mut RenderPhase<TransparentUI>)>, + mut views: Query<(Entity, &mut RenderPhase<TransparentUI>, &ExtractedView)>, mut image_bind_groups: ResMut<ImageBindGroups>, unified_pipeline: Res<UnifiedPipeline>, gpu_images: Res<RenderAssets<Image>>, - msaa: Res<Msaa>, ) { if let Some(type_binding) = sprite_meta.types_buffer.binding() { sprite_meta.types_bind_group = @@ -562,13 +555,16 @@ pub fn queue_quads( layout: &quad_pipeline.view_layout, })); - let key = UnifiedPipelineKey::from_msaa_samples(msaa.samples); - let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key); - let draw_quad = draw_functions.read().get_id::<DrawUI>().unwrap(); - for (camera_entity, mut transparent_phase) in views.iter_mut() { + for (camera_entity, mut transparent_phase, view) in views.iter_mut() { + let key = UnifiedPipelineKey { + msaa: 1, + hdr: view.hdr, + }; + let spec_pipeline = pipelines.specialize(&mut pipeline_cache, &quad_pipeline, key); + for (entity, quad) in extracted_sprites.iter_mut() { - if quad.camera_entity != camera_entity { + if camera_entity != quad.camera_entity { continue; } if let Some(image_handle) = quad.image.as_ref() { @@ -596,6 +592,7 @@ pub fn queue_quads( }); } } + transparent_phase.add(TransparentUI { draw_function: draw_quad, pipeline: spec_pipeline, diff --git a/src/render/unified/shader.wgsl b/src/render/unified/shader.wgsl index cc6061d4ceb11fffbbac17adbff3e94f22de3a0b..122cfb872ee9a1025487f9500e67e9032db2f1bf 100644 --- a/src/render/unified/shader.wgsl +++ b/src/render/unified/shader.wgsl @@ -53,7 +53,7 @@ var image_texture: texture_2d<f32>; @group(3) @binding(1) var image_sampler: sampler; -let RADIUS: f32 = 0.1; +const RADIUS: f32 = 0.1; // Where P is the position in pixel space, B is the size of the box adn R is the radius of the current corner. fn sdRoundBox(p: vec2<f32>, b: vec2<f32>, r: f32) -> f32 { @@ -71,7 +71,7 @@ fn sample_sdf(coords: vec2<f32>, arr: i32, scale: f32) -> f32 { } fn range_curve(font_size: f32) -> f32 { - return (8.528 - 9.428 * font_size + 3.428 * pow(font_size, 2.0)) + 1.0; + return (5.128 - 6.428 * font_size + 3.428 * pow(font_size, 2.0)) + 1.0; } @fragment diff --git a/src/render/unified/text.rs b/src/render/unified/text.rs index 400917b415b30fa03654660d8d50548935a1fe76..c9fdb936706fc046f8b86939347076b3995df693 100644 --- a/src/render/unified/text.rs +++ b/src/render/unified/text.rs @@ -1,10 +1,10 @@ use bevy::{ - prelude::{Plugin, Res, ResMut}, + prelude::{IntoSystemConfig, Plugin, Res, ResMut}, render::{ render_asset::RenderAssets, renderer::{RenderDevice, RenderQueue}, texture::Image, - RenderApp, RenderStage, + RenderApp, RenderSet, }, }; use kayak_font::bevy::{FontTextureCache, KayakFontPlugin}; @@ -19,7 +19,7 @@ impl Plugin for TextRendererPlugin { app.add_plugin(KayakFontPlugin); let render_app = app.sub_app_mut(RenderApp); - render_app.add_system_to_stage(RenderStage::Queue, create_and_update_font_cache_texture); + render_app.add_system(create_and_update_font_cache_texture.in_set(RenderSet::Queue)); } } fn create_and_update_font_cache_texture( diff --git a/src/widget_context.rs b/src/widget_context.rs index c6e64c770389ec4737fb9c45fd6fd9ada62f9f69..5f5b95ed41c68cc876f0abd06ff53e345c68d4b0 100644 --- a/src/widget_context.rs +++ b/src/widget_context.rs @@ -225,6 +225,21 @@ impl KayakWidgetContext { } } + // Despawns a widget entity and it's decedents. This is done in a safe way by keeping entity id's around. + pub fn despawn_safe(&self, commands: &mut Commands, entity: Entity) { + if let Ok(mut tree) = self.old_tree.write() { + let mut down_iter = tree.down_iter(); + down_iter.current_node = Some(WrappedIndex(entity)); + for child in down_iter { + commands.entity(child.0).despawn(); + commands.get_or_spawn(child.0); + } + commands.entity(entity).despawn(); + commands.get_or_spawn(entity); + tree.remove(WrappedIndex(entity)); + } + } + /// Attempts to get the layout rect for the widget with the given ID /// /// # Arguments diff --git a/src/widgets/app.rs b/src/widgets/app.rs index 6a0ba1106e844fc6ecfd24b885c86fadb0020837..4b4aa9620f48e675b5cfed4cb1fe2a9fa7d7fd9e 100644 --- a/src/widgets/app.rs +++ b/src/widgets/app.rs @@ -1,4 +1,4 @@ -use bevy::prelude::*; +use bevy::{prelude::*, window::PrimaryWindow}; use kayak_ui_macros::rsx; use crate::{ @@ -45,7 +45,7 @@ pub fn app_update( In((widget_context, entity, previous_props_entity)): In<(KayakWidgetContext, Entity, Entity)>, widget_param: WidgetParam<KayakApp, EmptyState>, camera: Query<&Camera, With<CameraUIKayak>>, - windows: Res<Windows>, + windows: Query<&Window, With<PrimaryWindow>>, ) -> bool { let mut window_change = false; @@ -60,7 +60,7 @@ pub fn app_update( window_change = true; } } else { - let primary_window = windows.get_primary().unwrap(); + let primary_window = windows.single(); if app_style.0.width != StyleProp::Value(Units::Pixels(primary_window.width())) { window_change = true; @@ -84,8 +84,6 @@ pub fn app_render( mut commands: Commands, mut query: Query<(&KStyle, &mut ComputedStyles, &KChildren)>, camera: Query<&Camera, With<CameraUIKayak>>, - windows: Res<Windows>, - images: Res<Assets<Image>>, ) -> bool { let (mut width, mut height) = (0.0, 0.0); @@ -94,17 +92,9 @@ pub fn app_render( if let Some(size) = camera.logical_viewport_size() { width = size.x; height = size.y; - } else if let Some(viewport) = camera - .target - .get_render_target_info(&windows, &images) - .as_ref() - .map(|target_info| { - let scale = target_info.scale_factor; - (target_info.physical_size.as_dvec2() / scale).as_vec2() - }) - { - width = viewport.x; - height = viewport.y; + } else if let Some(viewport) = &camera.viewport { + width = viewport.physical_size.x as f32; + height = viewport.physical_size.y as f32; } } } diff --git a/src/widgets/button.rs b/src/widgets/button.rs index 96dee97f28e194aa1a34ccb1fd04b245c7fa5a81..571e74da1c7411f3e3927d1a02086a4b73c8461f 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -7,7 +7,7 @@ use kayak_ui_macros::rsx; use crate::{ context::WidgetName, - event::{Event, EventType}, + event::{EventType, KEvent}, event_dispatcher::EventDispatcherContext, on_event::OnEvent, prelude::{KChildren, KayakWidgetContext, Units}, @@ -92,7 +92,7 @@ pub fn button_render( move |In((event_dispatcher_context, _, mut event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut ButtonState>| { diff --git a/src/widgets/scroll/scroll_bar.rs b/src/widgets/scroll/scroll_bar.rs index e45f3a17c2c1ab1aa6dcaa664a56e095055e8b6e..f554548da664371634e73b4a6d86ddc4ff913d6e 100644 --- a/src/widgets/scroll/scroll_bar.rs +++ b/src/widgets/scroll/scroll_bar.rs @@ -3,7 +3,7 @@ use kayak_ui_macros::rsx; use crate::{ context::WidgetName, - event::{Event, EventType}, + event::{EventType, KEvent}, event_dispatcher::EventDispatcherContext, on_event::OnEvent, prelude::{KChildren, KayakWidgetContext}, @@ -206,7 +206,7 @@ pub fn scroll_bar_render( move |In((mut event_dispatcher_context, _, mut event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut ScrollContext>| { diff --git a/src/widgets/scroll/scroll_box.rs b/src/widgets/scroll/scroll_box.rs index 5d4643c537ba9268827b8c62c25e14ea778a6f08..c59d7c21f6f3c2d437879529c37f8f67b9cd7275 100644 --- a/src/widgets/scroll/scroll_box.rs +++ b/src/widgets/scroll/scroll_box.rs @@ -4,7 +4,7 @@ use crate::{ children::KChildren, context::WidgetName, cursor::ScrollUnit, - event::{Event, EventType}, + event::{EventType, KEvent}, event_dispatcher::EventDispatcherContext, layout::{GeometryChanged, LayoutEvent}, on_event::OnEvent, @@ -185,7 +185,7 @@ pub fn scroll_box_render( move |In((event_dispatcher_context, _, mut event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut ScrollContext>| { diff --git a/src/widgets/text_box.rs b/src/widgets/text_box.rs index c0426e4634fdee43312e63d9b2fd1e272a34bc14..67cf244e99545786fe1b338a634bdf0d0d8e65c2 100644 --- a/src/widgets/text_box.rs +++ b/src/widgets/text_box.rs @@ -6,7 +6,7 @@ use kayak_ui_macros::{constructor, rsx}; use crate::{ context::WidgetName, - event::{Event, EventType}, + event::{EventType, KEvent}, event_dispatcher::EventDispatcherContext, on_event::OnEvent, on_layout::OnLayout, @@ -185,7 +185,7 @@ pub fn text_box_render( move |In((event_dispatcher_context, _, mut event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, font_assets: Res<Assets<KayakFont>>, diff --git a/src/widgets/window.rs b/src/widgets/window.rs index dd00fd48e1ce22be161077d48279697377c33274..b362deb29e1251a21d7be0ab7dd985507e57a065 100644 --- a/src/widgets/window.rs +++ b/src/widgets/window.rs @@ -7,7 +7,7 @@ use kayak_ui_macros::rsx; use crate::{ children::KChildren, context::WidgetName, - event::{Event, EventType}, + event::{EventType, KEvent}, event_dispatcher::EventDispatcherContext, on_event::OnEvent, prelude::KayakWidgetContext, @@ -132,7 +132,7 @@ pub fn window_render( move |In((event_dispatcher_context, _, mut event, _entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut KWindowState>, @@ -226,7 +226,7 @@ pub fn window_render( move |In((mut event_dispatcher_context, _, mut event, entity)): In<( EventDispatcherContext, WidgetState, - Event, + KEvent, Entity, )>, mut query: Query<&mut KWindowState>| { diff --git a/src/window_size.rs b/src/window_size.rs index 4b17d24245df8e39d9d3ef4236618f6b5e518b58..dd274d5aee0c6ff946a18e7e923310895f2370a5 100644 --- a/src/window_size.rs +++ b/src/window_size.rs @@ -10,32 +10,42 @@ pub struct WindowSize(pub f32, pub f32); pub fn update_window_size( mut window_resized_events: EventReader<WindowResized>, mut window_created_events: EventReader<WindowCreated>, - windows: Res<Windows>, + windows: Query<&Window>, mut window_size: ResMut<WindowSize>, ) { - let mut changed_window_ids = Vec::new(); + let mut changed_windows = Vec::new(); // handle resize events. latest events are handled first because we only want to resize each // window once - for event in window_resized_events.iter().rev() { - if changed_window_ids.contains(&event.id) { + for event in window_resized_events + .iter() + .collect::<Vec<_>>() + .iter() + .rev() + { + if changed_windows.contains(&event.window) { continue; } - changed_window_ids.push(event.id); + changed_windows.push(event.window); } // handle resize events. latest events are handled first because we only want to resize each // window once - for event in window_created_events.iter().rev() { - if changed_window_ids.contains(&event.id) { + for event in window_created_events + .iter() + .collect::<Vec<_>>() + .iter() + .rev() + { + if changed_windows.contains(&event.window) { continue; } - changed_window_ids.push(event.id); + changed_windows.push(event.window); } - for window_id in changed_window_ids { - if let Some(window) = windows.get(window_id) { + for window_entity in changed_windows { + if let Ok(window) = windows.get(window_entity) { let width = window.width(); let height = window.height(); *window_size = WindowSize(width, height);