diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 591bb1d5..800e0c54 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -20,7 +20,7 @@ jobs: steps: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.76.0 + toolchain: 1.79.0 components: clippy, rustfmt - uses: actions/checkout@v3 - uses: actions/cache@v3 @@ -31,7 +31,7 @@ jobs: ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ - key: ${{ runner.os }}-cargo-build-1.76.0-${{ hashFiles('**/Cargo.toml') }} + key: ${{ runner.os }}-cargo-build-1.79.0-${{ hashFiles('**/Cargo.toml') }} - name: Install alsa and udev run: sudo apt-get update; sudo apt-get install --no-install-recommends libasound2-dev libudev-dev libwayland-dev libxkbcommon-dev if: runner.os == 'linux' diff --git a/Cargo.toml b/Cargo.toml index 100044e4..bb5d53b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kayak_ui" description = "A UI library built using the bevy game engine!" -version = "0.5.0" +version = "0.6.0" edition = "2021" resolver = "2" authors = ["John Mitchell"] @@ -14,12 +14,12 @@ exclude = ["assets/*", "screenshots/*", "book"] members = ["kayak_ui_macros", "kayak_font"] [features] -svg = ["dep:bevy_svg"] +# svg = ["dep:bevy_svg"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bevy = { version = "0.13", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_core_pipeline"] } +bevy = { version = "0.14", default-features = false, features = ["bevy_render", "bevy_asset", "bevy_core_pipeline"] } bevy_svg = { git = "https://github.com/arnfaldur/bevy_svg", rev="53a53e5af050a7b5b236068546be46c5729674e3", default-features = false, optional = true } bitflags = "1.3.2" bytemuck = "1.12" @@ -28,8 +28,8 @@ fancy-regex = "0.11.0" indexmap = "1.9" instant = "0.1" interpolation = { version = "0.2" } -kayak_font = { path = "./kayak_font", version = "0.5" } -kayak_ui_macros = { path = "./kayak_ui_macros", version = "0.5" } +kayak_font = { path = "./kayak_font", version = "0.6" } +kayak_ui_macros = { path = "./kayak_ui_macros", version = "0.6" } log = "0.4" morphorm = "0.3" reorder = "2.1" @@ -40,8 +40,8 @@ smol_str = {version = "0.2", default-features = false} [dev-dependencies] fastrand = "1.8" -bevy-inspector-egui = "0.23" -bevy = { version = "0.13", default-features = true } +bevy-inspector-egui = "0.25" +bevy = { version = "0.14", default-features = true } [[example]] name = "tabs" @@ -51,21 +51,27 @@ path = "examples/tabs/tabs.rs" name = "todo" path = "examples/todo/todo.rs" +[[example]] +name = "main_menu" +path = "examples/main_menu.rs" + [[example]] name = "svg" -path = "examples/svg.rs" +# path = "examples/svg.rs" +path = "examples/main_menu.rs" test = false doc = false bench = false -required-features = ["svg"] +# required-features = ["svg"] [[example]] name = "accordion" -path = "examples/accordion.rs" +# path = "examples/accordion.rs" +path = "examples/main_menu.rs" test = false doc = false bench = false -required-features = ["svg"] +# required-features = ["svg"] [package.metadata.docs.rs] features = ["bevy/x11"] diff --git a/examples/avsb.rs b/examples/avsb.rs index 544df759..7df6d83a 100644 --- a/examples/avsb.rs +++ b/examples/avsb.rs @@ -223,5 +223,5 @@ fn main() { )) .add_systems(Startup, startup) .add_systems(Update, swap) - .run() + .run(); } diff --git a/examples/bevy_scene.rs b/examples/bevy_scene.rs index 705938fc..a37c4210 100644 --- a/examples/bevy_scene.rs +++ b/examples/bevy_scene.rs @@ -1,4 +1,5 @@ use bevy::{ + color::palettes::css::{INDIGO, MAROON, TEAL}, math::{Vec3Swizzles, Vec4Swizzles}, prelude::*, window::PrimaryWindow, @@ -6,7 +7,7 @@ use bevy::{ use kayak_ui::prelude::{widgets::*, *}; const TILE_SIZE: Vec2 = Vec2::from_array([50.0, 50.0]); -const COLORS: &[Color] = &[Color::TEAL, Color::MAROON, Color::INDIGO]; +const COLORS: &[Srgba] = &[TEAL, MAROON, INDIGO]; // ! === Unnecessary Details Below === ! // // Below this point are mainly implementation details. The main purpose of this example is to show how to know @@ -108,10 +109,10 @@ fn on_color_change( } let mut active_tile = active_tile.single_mut(); - active_tile.color = COLORS[active_color.index]; + active_tile.color = COLORS[active_color.index].into(); let mut ghost_tile = ghost_tile.single_mut(); - ghost_tile.color = ghost_color(COLORS[active_color.index]); + ghost_tile.color = ghost_color(COLORS[active_color.index].into()); } /// A system that sets up the world @@ -119,7 +120,7 @@ fn world_setup(mut commands: Commands, active_color: Res) { commands .spawn(SpriteBundle { sprite: Sprite { - color: COLORS[active_color.index], + color: COLORS[active_color.index].into(), custom_size: Some(TILE_SIZE), ..Default::default() }, @@ -129,7 +130,7 @@ fn world_setup(mut commands: Commands, active_color: Res) { commands .spawn(SpriteBundle { sprite: Sprite { - color: ghost_color(COLORS[active_color.index]), + color: ghost_color(COLORS[active_color.index].into()), custom_size: Some(TILE_SIZE), ..Default::default() }, @@ -160,7 +161,7 @@ fn world_to_tile(world_pos: Vec2) -> Vec2 { /// Get the ghost tile color for a given color fn ghost_color(color: Color) -> Color { let mut c = color; - c.set_a(0.35); + c.set_alpha(0.35); c } @@ -257,7 +258,7 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .insert_resource(ActiveColor { index: 0 }) .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) @@ -271,5 +272,5 @@ fn main() { on_color_change, ), ) - .run() + .run(); } diff --git a/examples/box_shadows.rs b/examples/box_shadows.rs index d1c656e9..ab4f3047 100644 --- a/examples/box_shadows.rs +++ b/examples/box_shadows.rs @@ -34,7 +34,7 @@ fn startup( height: Units::Pixels(182.0).into(), box_shadow: vec![ BoxShadow { - color: Color::rgb(38.0 / 255.0, 57.0 / 255.0, 77.0 / 255.0), + color: Color::srgb(38.0 / 255.0, 57.0 / 255.0, 77.0 / 255.0), radius: 30.0, offset: Vec2::new(0.0, 20.0), spread: Vec2::new(0.0, -10.0), @@ -53,7 +53,7 @@ fn startup( height: Units::Pixels(182.0).into(), box_shadow: vec![ BoxShadow { - color: Color::rgba(0.0, 0.0, 0.0, 0.1), + color: Color::srgba(0.0, 0.0, 0.0, 0.1), radius: 12.0, offset: Vec2::new(0.0, 4.0), spread: Vec2::new(0.0, 0.0), @@ -175,9 +175,9 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(1.0, 1.0, 1.0))) + .insert_resource(ClearColor(Color::srgb(1.0, 1.0, 1.0))) .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/clipping.rs b/examples/clipping.rs index b59b2cfe..178841b3 100644 --- a/examples/clipping.rs +++ b/examples/clipping.rs @@ -66,9 +66,9 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/conditional_widget.rs b/examples/conditional_widget.rs index 54f1451a..b94e1854 100644 --- a/examples/conditional_widget.rs +++ b/examples/conditional_widget.rs @@ -130,9 +130,9 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/context.rs b/examples/context.rs index 8ba6720b..977f468b 100644 --- a/examples/context.rs +++ b/examples/context.rs @@ -23,25 +23,25 @@ impl Theme { fn vampire() -> Self { Self { name: "Vampire".to_string(), - primary: Color::rgba(1.0, 0.475, 0.776, 1.0), - secondary: Color::rgba(0.641, 0.476, 0.876, 1.0), - background: Color::rgba(0.157, 0.165, 0.212, 1.0), + primary: Color::srgba(1.0, 0.475, 0.776, 1.0), + secondary: Color::srgba(0.641, 0.476, 0.876, 1.0), + background: Color::srgba(0.157, 0.165, 0.212, 1.0), } } fn solar() -> Self { Self { name: "Solar".to_string(), - primary: Color::rgba(0.514, 0.580, 0.588, 1.0), - secondary: Color::rgba(0.149, 0.545, 0.824, 1.0), - background: Color::rgba(0.026, 0.212, 0.259, 1.0), + primary: Color::srgba(0.514, 0.580, 0.588, 1.0), + secondary: Color::srgba(0.149, 0.545, 0.824, 1.0), + background: Color::srgba(0.026, 0.212, 0.259, 1.0), } } fn vector() -> Self { Self { name: "Vector".to_string(), - primary: Color::rgba(0.533, 1.0, 0.533, 1.0), - secondary: Color::rgba(0.098, 0.451, 0.098, 1.0), - background: Color::rgba(0.004, 0.059, 0.004, 1.0), + primary: Color::srgba(0.533, 1.0, 0.533, 1.0), + secondary: Color::srgba(0.098, 0.451, 0.098, 1.0), + background: Color::srgba(0.004, 0.059, 0.004, 1.0), } } } @@ -380,5 +380,5 @@ fn main() { .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/custom_shader.rs b/examples/custom_shader.rs index 99a2403b..ea1aea0f 100644 --- a/examples/custom_shader.rs +++ b/examples/custom_shader.rs @@ -64,5 +64,5 @@ fn main() { MaterialUIPlugin::::default(), )) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/demo.rs b/examples/demo.rs index 0b235180..fa76cfd3 100644 --- a/examples/demo.rs +++ b/examples/demo.rs @@ -73,5 +73,5 @@ fn main() { .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) .add_systems(Update, update_resource) - .run() + .run(); } diff --git a/examples/font_size_test.rs b/examples/font_size_test.rs index 9e7a57b8..3c83934d 100644 --- a/examples/font_size_test.rs +++ b/examples/font_size_test.rs @@ -56,5 +56,5 @@ fn main() { .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/hello_world.rs b/examples/hello_world.rs index 79915cab..d3c303a8 100644 --- a/examples/hello_world.rs +++ b/examples/hello_world.rs @@ -40,5 +40,5 @@ fn main() { .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/hello_world_no_macro.rs b/examples/hello_world_no_macro.rs index 91825efb..cffe7c32 100644 --- a/examples/hello_world_no_macro.rs +++ b/examples/hello_world_no_macro.rs @@ -51,5 +51,5 @@ fn main() { .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/image.rs b/examples/image.rs index 86c5c7e7..76350669 100644 --- a/examples/image.rs +++ b/examples/image.rs @@ -42,5 +42,5 @@ fn main() { .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/layout.rs b/examples/layout.rs index 85e331b6..aacacafe 100644 --- a/examples/layout.rs +++ b/examples/layout.rs @@ -37,8 +37,8 @@ fn startup( > , event: ResMut, mut exit: EventWriter| { if let EventType::Click(..) = event.event_type { - exit.send(AppExit); + exit.send(AppExit::Success); } }, ); @@ -219,5 +219,5 @@ fn main() { .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/modal.rs b/examples/modal.rs index 789a23f2..24188e59 100644 --- a/examples/modal.rs +++ b/examples/modal.rs @@ -155,7 +155,7 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) .add_plugins(( KayakContextPlugin, @@ -163,5 +163,5 @@ fn main() { MaterialUIPlugin::::default(), )) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/nine_patch.rs b/examples/nine_patch.rs index 90d44f59..3ab054eb 100644 --- a/examples/nine_patch.rs +++ b/examples/nine_patch.rs @@ -64,5 +64,5 @@ fn main() { .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/opacity_layers.rs b/examples/opacity_layers.rs index 32afe69d..498edb21 100644 --- a/examples/opacity_layers.rs +++ b/examples/opacity_layers.rs @@ -54,9 +54,9 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/quads.rs b/examples/quads.rs index ac3a910f..48bdac60 100644 --- a/examples/quads.rs +++ b/examples/quads.rs @@ -113,7 +113,7 @@ fn startup( fastrand::i32(32..64) as f32, fastrand::i32(32..64) as f32, ); - let color = Color::rgba( + let color = Color::srgba( fastrand::f32(), fastrand::f32(), fastrand::f32(), @@ -142,5 +142,5 @@ fn main() { KayakWidgets, )) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/render_target.rs b/examples/render_target.rs index d1517509..868f6225 100644 --- a/examples/render_target.rs +++ b/examples/render_target.rs @@ -184,5 +184,5 @@ fn main() { .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) .add_systems(Update, (cube_rotator_system, depsawn_ui)) - .run() + .run(); } diff --git a/examples/scrolling.rs b/examples/scrolling.rs index 80ad221f..cce8217a 100644 --- a/examples/scrolling.rs +++ b/examples/scrolling.rs @@ -68,7 +68,7 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed tellus neque. fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins(( KayakContextPlugin, @@ -77,5 +77,5 @@ fn main() { FrameTimeDiagnosticsPlugin, )) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/simple_state.rs b/examples/simple_state.rs index ef5f38a0..683be05b 100644 --- a/examples/simple_state.rs +++ b/examples/simple_state.rs @@ -136,9 +136,9 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/tabs/tab.rs b/examples/tabs/tab.rs index 02ab7b59..9f1ced8a 100644 --- a/examples/tabs/tab.rs +++ b/examples/tabs/tab.rs @@ -49,7 +49,7 @@ pub fn tab_render( styles.0.width = StyleProp::default(); let parent_id = Some(entity); let styles = KStyle { - background_color: StyleProp::Value(Color::rgba(0.0781, 0.0898, 0.101, 1.0)), + background_color: StyleProp::Value(Color::srgba(0.0781, 0.0898, 0.101, 1.0)), padding: StyleProp::Value(Edge::all(Units::Pixels(15.0))), height: Units::Stretch(1.0).into(), width: Units::Stretch(1.0).into(), diff --git a/examples/tabs/tab_button.rs b/examples/tabs/tab_button.rs index 6c772632..2fe215d2 100644 --- a/examples/tabs/tab_button.rs +++ b/examples/tabs/tab_button.rs @@ -47,9 +47,9 @@ pub fn tab_button_render( .unwrap(); if let Ok(tab_context) = tab_context_query.get(context_entity) { let background_color = if tab_context.current_index == tab_button.index { - Color::rgba(0.0781, 0.0898, 0.101, 1.0) + Color::srgba(0.0781, 0.0898, 0.101, 1.0) } else { - Color::rgba(0.0781, 0.0898, 0.101, 0.75) + Color::srgba(0.0781, 0.0898, 0.101, 0.75) }; let parent_id = Some(entity); diff --git a/examples/tabs/tabs.rs b/examples/tabs/tabs.rs index 674ba0bc..e10142bd 100644 --- a/examples/tabs/tabs.rs +++ b/examples/tabs/tabs.rs @@ -92,10 +92,10 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) // .add_plugin(bevy_inspector_egui::quick::WorldInspectorPlugin::new()) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/text.rs b/examples/text.rs index e87b8b7e..3bc5f333 100644 --- a/examples/text.rs +++ b/examples/text.rs @@ -18,7 +18,7 @@ fn my_widget_1_render( // Note: We will see two updates because of the mutable change to styles. // Which means when foo changes MyWidget will render twice! *computed_styles = KStyle { - color: Color::RED.into(), + color: Color::srgb(1., 0., 0.).into(), render_command: StyleProp::Value(RenderCommand::Text { content: format!("My number is: {}", my_widget.foo), alignment: Alignment::Start, @@ -113,5 +113,5 @@ fn main() { .insert_resource(MyResource(1)) .add_systems(Startup, startup) .add_systems(Update, update_resource) - .run() + .run(); } diff --git a/examples/text_box.rs b/examples/text_box.rs index 0960c933..acb08d1a 100644 --- a/examples/text_box.rs +++ b/examples/text_box.rs @@ -126,9 +126,9 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/texture_atlas.rs b/examples/texture_atlas.rs index 01ce4d47..c3d381ee 100644 --- a/examples/texture_atlas.rs +++ b/examples/texture_atlas.rs @@ -18,13 +18,7 @@ fn startup( let tile_size = 16; let columns = 272 / tile_size; let rows = 128 / tile_size; - let atlas = bevy::sprite::TextureAtlasLayout::from_grid( - bevy::prelude::Vec2::splat(tile_size as f32), - columns, - rows, - None, - None, - ); + let atlas = TextureAtlasLayout::from_grid(UVec2::splat(tile_size), columns, rows, None, None); //The sign in the top right of the image would be index 16 let sign_index = 16; @@ -46,7 +40,7 @@ fn startup( let sign_position = rect.min; let sign_size = rect.max - rect.min; - let rect = atlas.textures[flower_index]; + let rect = atlas.textures[flower_index as usize]; let flower_position = rect.min; let flower_size = rect.max - rect.min; @@ -79,5 +73,5 @@ fn main() { .add_plugins(DefaultPlugins.set(ImagePlugin::default_nearest())) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/todo/items.rs b/examples/todo/items.rs index 80f8e2e3..8c2f4791 100644 --- a/examples/todo/items.rs +++ b/examples/todo/items.rs @@ -60,7 +60,7 @@ pub fn render_todo_items( @@ -128,12 +128,12 @@ fn startup( looping: true, style_a: KStyle { render_command: RenderCommand::Quad.into(), - background_color: Color::rgba(1.0, 0.0, 0.0, 1.0).into(), + background_color: Color::srgba(1.0, 0.0, 0.0, 1.0).into(), ..Default::default() }, style_b: KStyle { render_command: RenderCommand::Quad.into(), - background_color: Color::rgba(0.0, 0.0, 1.0, 1.0).into(), + background_color: Color::srgba(0.0, 0.0, 1.0, 1.0).into(), ..Default::default() }, ..Default::default() @@ -161,7 +161,7 @@ fn startup( @@ -183,5 +183,5 @@ fn main() { FrameTimeDiagnosticsPlugin, )) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/examples/vec.rs b/examples/vec.rs index d0a60707..dbb5756d 100644 --- a/examples/vec.rs +++ b/examples/vec.rs @@ -82,9 +82,9 @@ fn startup( fn main() { App::new() - .insert_resource(ClearColor(Color::rgb(0.0, 0.0, 0.0))) + .insert_resource(ClearColor(Color::srgb(0.0, 0.0, 0.0))) .add_plugins(DefaultPlugins) .add_plugins((KayakContextPlugin, KayakWidgets)) .add_systems(Startup, startup) - .run() + .run(); } diff --git a/kayak_font/Cargo.toml b/kayak_font/Cargo.toml index 3354ddd3..174d95bd 100644 --- a/kayak_font/Cargo.toml +++ b/kayak_font/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kayak_font" description = "An SDF font renderer for Kayak UI and the Bevy game engine" -version = "0.5.0" +version = "0.6.0" edition = "2021" resolver = "2" authors = ["John Mitchell"] @@ -28,10 +28,10 @@ image = "0.24" # Provides UAX #14 line break segmentation xi-unicode = "0.3" -bevy = { version = "0.13", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] } +bevy = { version = "0.14", optional = true, default-features = false, features = ["bevy_asset", "bevy_render", "bevy_core_pipeline"] } [dev-dependencies] -bevy = { version = "0.13", default-features = false } +bevy = { version = "0.14", default-features = false } bytemuck = "1.12.0" [package.metadata.docs.rs] diff --git a/kayak_font/src/bevy/loader.rs b/kayak_font/src/bevy/loader.rs index 84c95368..f4f481ea 100644 --- a/kayak_font/src/bevy/loader.rs +++ b/kayak_font/src/bevy/loader.rs @@ -1,5 +1,5 @@ use crate::{ImageType, KayakFont, Sdf}; -use bevy::asset::{io::Reader, AssetLoader, AssetPath, AsyncReadExt, BoxedFuture, LoadContext}; +use bevy::asset::{io::Reader, AssetLoader, AssetPath, AsyncReadExt, LoadContext}; #[derive(Default)] pub struct KayakFontLoader; @@ -10,25 +10,23 @@ impl AssetLoader for KayakFontLoader { type Settings = (); type Error = anyhow::Error; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let path = load_context.asset_path().path(); - let path = path.with_extension("png"); - let atlas_image_path = AssetPath::from_path(&path); - let mut image_asset_context = load_context.begin_labeled_asset(); - let image_handle = image_asset_context.load(atlas_image_path); + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let path = load_context.asset_path().path(); + let path = path.with_extension("png"); + let atlas_image_path = AssetPath::from_path(&path); + let mut image_asset_context = load_context.begin_labeled_asset(); + let image_handle = image_asset_context.load(atlas_image_path); - let mut bytes = vec![]; - let _ = reader.read_to_end(&mut bytes).await; - let font = KayakFont::new(Sdf::from_bytes(&bytes), ImageType::Atlas(image_handle)); + let mut bytes = vec![]; + let _ = reader.read_to_end(&mut bytes).await; + let font = KayakFont::new(Sdf::from_bytes(&bytes), ImageType::Atlas(image_handle)); - Ok(font) - }) + Ok(font) } fn extensions(&self) -> &[&str] { diff --git a/kayak_font/src/bevy/renderer/font_texture_cache.rs b/kayak_font/src/bevy/renderer/font_texture_cache.rs index af4369f6..e2a36335 100644 --- a/kayak_font/src/bevy/renderer/font_texture_cache.rs +++ b/kayak_font/src/bevy/renderer/font_texture_cache.rs @@ -1,7 +1,7 @@ use crate::{ImageType, KayakFont, Sdf}; use bevy::{ asset::Handle, - math::Vec2, + math::{UVec2, Vec2}, prelude::{Res, Resource}, render::{ render_asset::RenderAssets, @@ -12,7 +12,7 @@ use bevy::{ TextureViewDimension, }, renderer::{RenderDevice, RenderQueue}, - texture::{GpuImage, Image}, + texture::GpuImage, }, utils::HashMap, }; @@ -60,7 +60,7 @@ impl FontTextureCache { pub fn get_gpu_image<'s>( &'s self, handle: &Handle, - render_images: &'s RenderAssets, + render_images: &'s RenderAssets, ) -> Option<&'s GpuImage> { if let Some(gpu_image) = self.images.get(handle) { Some(gpu_image) @@ -75,7 +75,7 @@ impl FontTextureCache { &mut self, device: &RenderDevice, queue: &RenderQueue, - render_images: &Res>, + render_images: &Res>, ) { let new_fonts: Vec<_> = self.new_fonts.drain(..).collect(); for kayak_font_handle in new_fonts { @@ -161,9 +161,9 @@ impl FontTextureCache { sampler, texture_view, mip_level_count: 1, - size: Vec2 { - x: size.0 as f32, - y: size.1 as f32, + size: UVec2 { + x: size.0, + y: size.1, }, texture_format: format, }; @@ -208,7 +208,7 @@ impl FontTextureCache { sampler, texture_view, mip_level_count: 1, - size: Vec2 { x: 1.0, y: 1.0 }, + size: UVec2 { x: 1, y: 1 }, texture_format: TextureFormat::Rgba8Unorm, } } diff --git a/kayak_font/src/ttf/loader.rs b/kayak_font/src/ttf/loader.rs index 41266a64..4bb5d3bd 100644 --- a/kayak_font/src/ttf/loader.rs +++ b/kayak_font/src/ttf/loader.rs @@ -8,7 +8,7 @@ use bevy::{ render_asset::RenderAssetUsages, render_resource::{Extent3d, TextureFormat}, }, - utils::{BoxedFuture, HashMap}, + utils::HashMap, }; use thiserror::Error; @@ -48,238 +48,234 @@ impl AssetLoader for TTFLoader { type Error = TTFLoaderError; - fn load<'a>( + async fn load<'a>( &'a self, - reader: &'a mut Reader, + reader: &'a mut Reader<'_>, _settings: &'a (), - load_context: &'a mut LoadContext, - ) -> BoxedFuture<'a, Result> { - Box::pin(async move { - let mut bytes = Vec::new(); - reader.read_to_end(&mut bytes).await?; - - let kttf: Kttf = - nanoserde::DeJson::deserialize_json(std::str::from_utf8(&bytes).unwrap()).unwrap(); - - let char_range_start = - u32::from_str_radix(kttf.char_range_start.trim_start_matches("0x"), 16).unwrap(); - let char_range_end = - u32::from_str_radix(kttf.char_range_end.trim_start_matches("0x"), 16).unwrap(); - - let mut cache_path = std::path::PathBuf::from(load_context.path()); - cache_path.set_file_name(kttf.file.clone()); - let font_bytes = load_context - .read_asset_bytes(cache_path.clone()) - .await - .unwrap(); - - let file_name = load_context - .path() - .file_name() - .unwrap() - .to_str() - .unwrap() - .to_string(); - cache_path.set_file_name(format!("{}-cached.png", file_name)); - let cache_image = load_context.read_asset_bytes(cache_path.clone()).await; - - let font_range = char_range_start..char_range_end; - let char_count = font_range.len() as u32; - - let size_x = 64usize; - let size_y = 128usize; - let face = ttf_parser::Face::parse(&font_bytes, 0).unwrap(); - let image_height = size_y as u32 * char_count; - let mut image_builder: RgbaImage = image::ImageBuffer::new(size_x as u32, image_height); - let mut yy = 0u32; - let mut glyphs = vec![]; - - // Build char to glyph mapping.. - let mut glyph_to_char: HashMap = - HashMap::with_capacity(face.number_of_glyphs() as usize); - let mut char_to_glyph: HashMap = - HashMap::with_capacity(face.number_of_glyphs() as usize); - if let Some(subtable) = face.tables().cmap { - for subtable in subtable.subtables { - subtable.codepoints(|codepoint| { - if let Some(mapping) = subtable.glyph_index(codepoint) { - glyph_to_char.insert(mapping, std::char::from_u32(codepoint).unwrap()); - char_to_glyph.insert(std::char::from_u32(codepoint).unwrap(), mapping); - } - }) - } + load_context: &'a mut LoadContext<'_>, + ) -> Result { + let mut bytes = Vec::new(); + reader.read_to_end(&mut bytes).await?; + + let kttf: Kttf = + nanoserde::DeJson::deserialize_json(std::str::from_utf8(&bytes).unwrap()).unwrap(); + + let char_range_start = + u32::from_str_radix(kttf.char_range_start.trim_start_matches("0x"), 16).unwrap(); + let char_range_end = + u32::from_str_radix(kttf.char_range_end.trim_start_matches("0x"), 16).unwrap(); + + let mut cache_path = std::path::PathBuf::from(load_context.path()); + cache_path.set_file_name(kttf.file.clone()); + let font_bytes = load_context + .read_asset_bytes(cache_path.clone()) + .await + .unwrap(); + + let file_name = load_context + .path() + .file_name() + .unwrap() + .to_str() + .unwrap() + .to_string(); + cache_path.set_file_name(format!("{}-cached.png", file_name)); + let cache_image = load_context.read_asset_bytes(cache_path.clone()).await; + + let font_range = char_range_start..char_range_end; + let char_count = font_range.len() as u32; + + let size_x = 64usize; + let size_y = 128usize; + let face = ttf_parser::Face::parse(&font_bytes, 0).unwrap(); + let image_height = size_y as u32 * char_count; + let mut image_builder: RgbaImage = image::ImageBuffer::new(size_x as u32, image_height); + let mut yy = 0u32; + let mut glyphs = vec![]; + + // Build char to glyph mapping.. + let mut glyph_to_char: HashMap = + HashMap::with_capacity(face.number_of_glyphs() as usize); + let mut char_to_glyph: HashMap = + HashMap::with_capacity(face.number_of_glyphs() as usize); + if let Some(subtable) = face.tables().cmap { + for subtable in subtable.subtables { + subtable.codepoints(|codepoint| { + if let Some(mapping) = subtable.glyph_index(codepoint) { + glyph_to_char.insert(mapping, std::char::from_u32(codepoint).unwrap()); + char_to_glyph.insert(std::char::from_u32(codepoint).unwrap(), mapping); + } + }) } + } - let loaded_file = &kttf; + let loaded_file = &kttf; - for char_u in font_range { - let c = char::from_u32(char_u).unwrap(); - let glyph_id = char_to_glyph.get(&c); - if glyph_id.is_none() { - continue; - } - let glyph_id = *glyph_id.unwrap(); - let mut output = FloatRGBBmp::new(size_x, size_y); - let mut builder = ShapeBuilder::default(); - let pixel_scale = size_x as f64 / face.units_per_em() as f64; - builder.pixel_scale = pixel_scale; - let _result = face.outline_glyph(glyph_id, &mut builder); - - let char_bounds = face - .glyph_bounding_box(glyph_id) - .unwrap_or(ttf_parser::Rect { - x_min: 0, - x_max: size_x as i16, - y_min: 0, - y_max: size_y as i16, - }); - - let mut shape = builder.build(); - shape.inverse_y_axis = true; - // let (left, bottom, right, top) = shape.get_bounds(); - - let scale = Vector2::new(1.0, 1.0); - let px_range = 8.0; - let range = px_range / scale.x.min(scale.y); - - let (translation, plane) = calculate_plane( - loaded_file, - &mut shape, - pixel_scale as f32, - 1.0, - px_range as f32, - 1.0, - ); - let advance = face.glyph_hor_advance(glyph_id).unwrap_or(0) as f32 / size_x as f32; - let c = *glyph_to_char.get(&glyph_id).unwrap(); - glyphs.push(Glyph { - unicode: c, - advance: advance * pixel_scale as f32, - atlas_bounds: Some(Rect { - left: 0.0, - bottom: 0.0, - right: size_x as f32, - top: size_y as f32, - }), - plane_bounds: Some(plane), + for char_u in font_range { + let c = char::from_u32(char_u).unwrap(); + let glyph_id = char_to_glyph.get(&c); + if glyph_id.is_none() { + continue; + } + let glyph_id = *glyph_id.unwrap(); + let mut output = FloatRGBBmp::new(size_x, size_y); + let mut builder = ShapeBuilder::default(); + let pixel_scale = size_x as f64 / face.units_per_em() as f64; + builder.pixel_scale = pixel_scale; + let _result = face.outline_glyph(glyph_id, &mut builder); + + let char_bounds = face + .glyph_bounding_box(glyph_id) + .unwrap_or(ttf_parser::Rect { + x_min: 0, + x_max: size_x as i16, + y_min: 0, + y_max: size_y as i16, }); - // let frame = Vector2::new(size_x as f64, size_y as f64); - - // dbg!((left, right, top, bottom)); - - // left = (left - (size_x as f64 / 8.0)).max(0.0); - // right = (right + (size_x as f64 / 8.0)).min(size_x as f64); - // top = (top + (size_y as f64 / 8.0)).min(size_y as f64); - // bottom = (bottom - (size_y as f64 / 8.0)).max(0.0); - - // dbg!((left, right, top, bottom)); - - // let dims = Vector2::new(right - left, top - bottom); - - // let translate = Vector2::new(-left + (frame.x - dims.x), (frame.y - (bottom + dims.y)) - 1.0); - if cache_image.is_err() { - msdf::edge_coloring::simple(&mut shape, 3.0, 0); - msdf::gen::generate_msdf( - &mut output, - &shape, - range, - scale, - translation + Vector2::new(0.0, size_x as f64 * 1.25), - 1.111_111_111_111_111_2, - ); + let mut shape = builder.build(); + shape.inverse_y_axis = true; + // let (left, bottom, right, top) = shape.get_bounds(); + + let scale = Vector2::new(1.0, 1.0); + let px_range = 8.0; + let range = px_range / scale.x.min(scale.y); + + let (translation, plane) = calculate_plane( + loaded_file, + &mut shape, + pixel_scale as f32, + 1.0, + px_range as f32, + 1.0, + ); + let advance = face.glyph_hor_advance(glyph_id).unwrap_or(0) as f32 / size_x as f32; + let c = *glyph_to_char.get(&glyph_id).unwrap(); + glyphs.push(Glyph { + unicode: c, + advance: advance * pixel_scale as f32, + atlas_bounds: Some(Rect { + left: 0.0, + bottom: 0.0, + right: size_x as f32, + top: size_y as f32, + }), + plane_bounds: Some(plane), + }); + + // let frame = Vector2::new(size_x as f64, size_y as f64); + + // dbg!((left, right, top, bottom)); + + // left = (left - (size_x as f64 / 8.0)).max(0.0); + // right = (right + (size_x as f64 / 8.0)).min(size_x as f64); + // top = (top + (size_y as f64 / 8.0)).min(size_y as f64); + // bottom = (bottom - (size_y as f64 / 8.0)).max(0.0); + + // dbg!((left, right, top, bottom)); + + // let dims = Vector2::new(right - left, top - bottom); + + // let translate = Vector2::new(-left + (frame.x - dims.x), (frame.y - (bottom + dims.y)) - 1.0); + if cache_image.is_err() { + msdf::edge_coloring::simple(&mut shape, 3.0, 0); + msdf::gen::generate_msdf( + &mut output, + &shape, + range, + scale, + translation + Vector2::new(0.0, size_x as f64 * 1.25), + 1.111_111_111_111_111_2, + ); - // let left = (translation.x - char_bounds.x_min as f64 * pixel_scale).max(0.0).floor() as u32; - let right = - (translation.x + char_bounds.x_max as f64 * pixel_scale).floor() as u32; - // let top = (translation.y - char_bounds.y_min as f64 * pixel_scale).max(0.0).floor() as u32; - let bottom = - (translation.y + char_bounds.y_max as f64 * pixel_scale).floor() as u32; - - for x in 0..(right + 2).min(64) { - for y in 0..bottom + 48 { - // for x in 0..size_x as u32 { - // for y in 0..size_y as u32 { - let pixel = output.get_pixel(x as usize, y as usize); - image_builder.put_pixel( - x, - yy + y, - image::Rgba([ - (pixel.r * 255.0) as u8, - (pixel.g * 255.0) as u8, - (pixel.b * 255.0) as u8, - 255, - ]), - ); - } + // let left = (translation.x - char_bounds.x_min as f64 * pixel_scale).max(0.0).floor() as u32; + let right = (translation.x + char_bounds.x_max as f64 * pixel_scale).floor() as u32; + // let top = (translation.y - char_bounds.y_min as f64 * pixel_scale).max(0.0).floor() as u32; + let bottom = + (translation.y + char_bounds.y_max as f64 * pixel_scale).floor() as u32; + + for x in 0..(right + 2).min(64) { + for y in 0..bottom + 48 { + // for x in 0..size_x as u32 { + // for y in 0..size_y as u32 { + let pixel = output.get_pixel(x as usize, y as usize); + image_builder.put_pixel( + x, + yy + y, + image::Rgba([ + (pixel.r * 255.0) as u8, + (pixel.g * 255.0) as u8, + (pixel.b * 255.0) as u8, + 255, + ]), + ); } } - // if c == '\"' { - // image_builder.save("test.png").unwrap(); - // panic!(""); - // } - yy += size_y as u32; } - - let image_bytes = match cache_image { - Ok(cache_image) => { - let image = image::load_from_memory(&cache_image).unwrap(); - image.as_bytes().to_vec() - } - Err(_) => { - #[cfg(not(target_family = "wasm"))] - { - let mut sources = AssetSourceBuilders::default(); - sources.init_default_source("assets", None); - let fake_server = AssetServer::new( - sources.build_sources(false, false), - bevy::asset::AssetServerMode::Unprocessed, - false, - ); - let writer = fake_server - .get_source(load_context.asset_path().source().clone()) - .unwrap() - .writer() - .unwrap(); - let mut cursor = std::io::Cursor::new(Vec::new()); - image_builder - .write_to(&mut cursor, image::ImageOutputFormat::Png) - .unwrap(); - - writer - .write_bytes(cache_path.as_path(), cursor.get_ref()) - .await - .unwrap(); - } - image_builder.as_bytes().to_vec() + // if c == '\"' { + // image_builder.save("test.png").unwrap(); + // panic!(""); + // } + yy += size_y as u32; + } + + let image_bytes = match cache_image { + Ok(cache_image) => { + let image = image::load_from_memory(&cache_image).unwrap(); + image.as_bytes().to_vec() + } + Err(_) => { + #[cfg(not(target_family = "wasm"))] + { + let mut sources = AssetSourceBuilders::default(); + sources.init_default_source("assets", None); + let fake_server = AssetServer::new( + sources.build_sources(false, false), + bevy::asset::AssetServerMode::Unprocessed, + false, + ); + let writer = fake_server + .get_source(load_context.asset_path().source().clone()) + .unwrap() + .writer() + .unwrap(); + let mut cursor = std::io::Cursor::new(Vec::new()); + image_builder + .write_to(&mut cursor, image::ImageOutputFormat::Png) + .unwrap(); + + writer + .write_bytes(cache_path.as_path(), cursor.get_ref()) + .await + .unwrap(); } - }; - - let mut sdf = Sdf::default(); - sdf.glyphs = glyphs; - sdf.atlas.font_size = size_x as f32; - - let mut image = bevy::prelude::Image::new( - Extent3d { - width: size_x as u32, - height: image_height, - depth_or_array_layers: 1, - }, - bevy::render::render_resource::TextureDimension::D2, - image_bytes, - TextureFormat::Rgba8Unorm, - RenderAssetUsages::all(), - ); - image.reinterpret_stacked_2d_as_array(char_count); - let labeled_asset = load_context.begin_labeled_asset(); - let loaded_image_asset = labeled_asset.finish(image, None); - let image_asset = - load_context.add_loaded_labeled_asset("font_image", loaded_image_asset); - - let font = KayakFont::new(sdf, ImageType::Array(image_asset)); + image_builder.as_bytes().to_vec() + } + }; - Ok(font) - }) + let mut sdf = Sdf::default(); + sdf.glyphs = glyphs; + sdf.atlas.font_size = size_x as f32; + + let mut image = bevy::prelude::Image::new( + Extent3d { + width: size_x as u32, + height: image_height, + depth_or_array_layers: 1, + }, + bevy::render::render_resource::TextureDimension::D2, + image_bytes, + TextureFormat::Rgba8Unorm, + RenderAssetUsages::all(), + ); + image.reinterpret_stacked_2d_as_array(char_count); + let labeled_asset = load_context.begin_labeled_asset(); + let loaded_image_asset = labeled_asset.finish(image, None); + let image_asset = load_context.add_loaded_labeled_asset("font_image", loaded_image_asset); + + let font = KayakFont::new(sdf, ImageType::Array(image_asset)); + + Ok(font) } fn extensions(&self) -> &[&str] { diff --git a/kayak_font/src/utility.rs b/kayak_font/src/utility.rs index 764fcc9a..f1c26a19 100644 --- a/kayak_font/src/utility.rs +++ b/kayak_font/src/utility.rs @@ -37,6 +37,7 @@ pub fn split_breakable_words(text: &str) -> BreakableWordIter { /// according to [UAX #14](https://www.unicode.org/reports/tr14/). #[derive(Copy, Clone, Debug)] pub struct BreakableWord<'a> { + #[allow(dead_code)] /// The index of the last character in this word. pub char_index: usize, /// The content of this word. diff --git a/kayak_ui_macros/Cargo.toml b/kayak_ui_macros/Cargo.toml index b824497c..34f4ec20 100644 --- a/kayak_ui_macros/Cargo.toml +++ b/kayak_ui_macros/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "kayak_ui_macros" description = "A proc macro library that provides RSX like syntax for Kayak UI." -version = "0.5.0" +version = "0.6.0" edition = "2021" resolver = "2" authors = ["John Mitchell"] diff --git a/kayak_ui_macros/src/tags.rs b/kayak_ui_macros/src/tags.rs index c88f66f3..45ac4752 100644 --- a/kayak_ui_macros/src/tags.rs +++ b/kayak_ui_macros/src/tags.rs @@ -8,6 +8,7 @@ pub struct OpenTag { pub name: Option, pub attributes: WidgetAttributes, pub self_closing: bool, + #[allow(dead_code)] pub is_custom_element: bool, } diff --git a/kayak_ui_macros/src/widget.rs b/kayak_ui_macros/src/widget.rs index 6b964560..c6d48edb 100644 --- a/kayak_ui_macros/src/widget.rs +++ b/kayak_ui_macros/src/widget.rs @@ -12,7 +12,9 @@ use crate::{tags::OpenTag, widget_attributes::WidgetAttributes}; #[derive(Clone, Debug)] pub struct Widget { + #[allow(dead_code)] pub attributes: WidgetAttributes, + #[allow(dead_code)] pub children: Children, declaration: TokenStream, pub entity_id: TokenStream, diff --git a/src/clone_component.rs b/src/clone_component.rs index 3ac130a9..79364980 100644 --- a/src/clone_component.rs +++ b/src/clone_component.rs @@ -1,4 +1,4 @@ -use bevy::{ecs::system::CommandQueue, prelude::*}; +use bevy::{ecs::world::CommandQueue, prelude::*}; use crate::widget_state::WidgetState; diff --git a/src/context.rs b/src/context.rs index 82b49160..1d5b7cf5 100644 --- a/src/context.rs +++ b/src/context.rs @@ -1,7 +1,7 @@ use std::sync::{Arc, RwLock}; use bevy::{ - ecs::{event::ManualEventReader, system::CommandQueue}, + ecs::{event::ManualEventReader, world::CommandQueue}, prelude::*, utils::{HashMap, HashSet}, window::PrimaryWindow, @@ -545,7 +545,7 @@ fn recurse_node_tree_to_build_primitives2( .unwrap(); extracted_quads.push(QuadOrMaterial::Quad(ExtractedQuad { camera_entity, - color: Color::rgba(1.0, 1.0, 1.0, opacity), + color: Color::srgba(1.0, 1.0, 1.0, opacity), opacity_layer, quad_type: UIQuadType::DrawOpacityLayer, rect: bevy::prelude::Rect { @@ -1479,9 +1479,6 @@ impl Plugin for KayakContextPlugin { .insert_resource(CustomEventReader(ManualEventReader::< bevy::input::mouse::MouseWheel, >::default())) - .insert_resource(CustomEventReader(ManualEventReader::< - bevy::window::ReceivedCharacter, - >::default())) .insert_resource(CustomEventReader(ManualEventReader::< bevy::input::keyboard::KeyboardInput, >::default())) diff --git a/src/event.rs b/src/event.rs index 67875301..255e8ba0 100644 --- a/src/event.rs +++ b/src/event.rs @@ -128,8 +128,6 @@ pub enum EventType { Focus, /// An event that occurs when a widget loses focus Blur, - /// An event that occurs when the user types in a character within a _focused_ widget - CharInput { c: smol_str::SmolStr }, /// An event that occurs when the user releases a key within a _focused_ widget KeyUp(KeyboardEvent), /// An event that occurs when the user presses a key down within a _focused_ widget @@ -176,7 +174,6 @@ impl EventType { Self::MouseDown(..) => true, Self::MouseUp(..) => true, Self::Scroll(..) => true, - Self::CharInput { .. } => true, Self::KeyUp(..) => true, Self::KeyDown(..) => true, // Doesn't Propagate @@ -199,7 +196,6 @@ impl EventType { Self::MouseOut(..) => EventCategory::Mouse, Self::Scroll(..) => EventCategory::Mouse, // Keyboard - Self::CharInput { .. } => EventCategory::Keyboard, Self::KeyUp(..) => EventCategory::Keyboard, Self::KeyDown(..) => EventCategory::Keyboard, // Focus diff --git a/src/event_dispatcher.rs b/src/event_dispatcher.rs index 98d3355d..68446261 100644 --- a/src/event_dispatcher.rs +++ b/src/event_dispatcher.rs @@ -692,11 +692,11 @@ impl EventDispatcher { let mut event_stream = Vec::new(); if let Some(current_focus) = focus_tree.current() { match input_event { - InputEvent::CharEvent { c } => event_stream.push(KEvent::new( - current_focus, - EventType::CharInput { c: c.clone() }, - )), - InputEvent::Keyboard { key, is_pressed } => { + InputEvent::Keyboard { + key, + logical_key, + is_pressed, + } => { // === Modifers === // match key { KeyCode::ControlLeft | KeyCode::ControlRight => { @@ -718,12 +718,20 @@ impl EventDispatcher { if *is_pressed { event_stream.push(KEvent::new( current_focus, - EventType::KeyDown(KeyboardEvent::new(*key, self.keyboard_modifiers)), + EventType::KeyDown(KeyboardEvent::new( + *key, + logical_key.clone(), + self.keyboard_modifiers, + )), )) } else { event_stream.push(KEvent::new( current_focus, - EventType::KeyUp(KeyboardEvent::new(*key, self.keyboard_modifiers)), + EventType::KeyUp(KeyboardEvent::new( + *key, + logical_key.clone(), + self.keyboard_modifiers, + )), )) } } diff --git a/src/input.rs b/src/input.rs index 2dccc461..07bb3cfa 100644 --- a/src/input.rs +++ b/src/input.rs @@ -21,12 +21,10 @@ pub(crate) fn process_events(world: &mut World) { Res>, Res>, Res>, - Res>, Res>, ResMut>, ResMut>, ResMut>, - ResMut>, ResMut>, ), _, @@ -36,12 +34,10 @@ pub(crate) fn process_events(world: &mut World) { cursor_moved_events, mouse_button_input_events, mouse_wheel_events, - char_input_events, keyboard_input_events, mut custom_event_reader_cursor, mut custom_event_mouse_button, mut custom_event_mouse_wheel, - mut custom_event_char_input, mut custom_event_keyboard, )| { if let Some(event) = custom_event_reader_cursor @@ -77,15 +73,10 @@ pub(crate) fn process_events(world: &mut World) { }) } - for event in custom_event_char_input.0.read(&char_input_events) { - input_events.push(InputEvent::CharEvent { - c: event.char.clone(), - }); - } - for event in custom_event_keyboard.0.read(&keyboard_input_events) { input_events.push(InputEvent::Keyboard { key: event.key_code, + logical_key: event.logical_key.clone(), is_pressed: matches!(event.state, ButtonState::Pressed), }); } diff --git a/src/input_event.rs b/src/input_event.rs index 7b4b3718..a8588261 100644 --- a/src/input_event.rs +++ b/src/input_event.rs @@ -1,4 +1,4 @@ -use bevy::prelude::KeyCode; +use bevy::{input::keyboard::Key, prelude::KeyCode}; /// Events sent to [`KayakContext`](crate::KayakContext) containing user input data #[derive(Debug, PartialEq)] @@ -11,10 +11,12 @@ pub enum InputEvent { MouseLeftRelease, /// An event that occurs when the user scrolls Scroll { dx: f32, dy: f32, is_line: bool }, - /// An event that occurs when the user types in a character - CharEvent { c: smol_str::SmolStr }, /// An event that occurs when the user presses or releases a key - Keyboard { key: KeyCode, is_pressed: bool }, + Keyboard { + key: KeyCode, + logical_key: Key, + is_pressed: bool, + }, } /// The various categories an input event can belong to @@ -36,7 +38,6 @@ impl InputEvent { Self::MouseLeftRelease => InputEventCategory::Mouse, Self::Scroll { .. } => InputEventCategory::Mouse, // Keyboard events - Self::CharEvent { .. } => InputEventCategory::Keyboard, Self::Keyboard { .. } => InputEventCategory::Keyboard, } } diff --git a/src/keyboard_event.rs b/src/keyboard_event.rs index 204be2fc..2c3f85c1 100644 --- a/src/keyboard_event.rs +++ b/src/keyboard_event.rs @@ -1,4 +1,4 @@ -use bevy::prelude::KeyCode; +use bevy::{input::keyboard::Key, prelude::KeyCode}; #[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] pub struct KeyboardModifiers { @@ -14,15 +14,20 @@ pub struct KeyboardModifiers { pub is_meta_pressed: bool, } -#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct KeyboardEvent { key: KeyCode, + logical_key: Key, modifiers: KeyboardModifiers, } impl KeyboardEvent { - pub fn new(key: KeyCode, modifiers: KeyboardModifiers) -> Self { - Self { key, modifiers } + pub fn new(key: KeyCode, logical_key: Key, modifiers: KeyboardModifiers) -> Self { + Self { + key, + logical_key, + modifiers, + } } /// Returns this event's affected key @@ -30,6 +35,11 @@ impl KeyboardEvent { self.key } + /// Returns the event's logical key + pub fn logical_key(&self) -> &Key { + &self.logical_key + } + /// Returns all modifiers for this event's key pub fn modifiers(&self) -> KeyboardModifiers { self.modifiers diff --git a/src/on_change.rs b/src/on_change.rs index ebeaab53..4653dc20 100644 --- a/src/on_change.rs +++ b/src/on_change.rs @@ -1,11 +1,10 @@ -use bevy::ecs::component::TableStorage; use bevy::prelude::{Component, Entity, In, IntoSystem, System, World}; use std::fmt::{Debug, Formatter}; use std::sync::{Arc, RwLock}; use crate::prelude::KayakWidgetContext; -pub trait ChangeValue: Component + Default {} +pub trait ChangeValue: Component + Default {} /// A container for a function that handles layout /// diff --git a/src/render/extract.rs b/src/render/extract.rs index b0e7de47..563258bc 100644 --- a/src/render/extract.rs +++ b/src/render/extract.rs @@ -177,7 +177,7 @@ pub struct UIViewUniform { pub world_position: Vec3, pub viewport: Vec4, pub frustum: [Vec4; 6], - pub color_grading: ColorGrading, + // pub color_grading: ColorGrading, pub mip_bias: f32, } @@ -231,7 +231,7 @@ pub fn prepare_view_uniforms( inverse_projection, world_position: camera.transform.translation(), viewport, - color_grading: camera.color_grading, + // color_grading: camera.color_grading, mip_bias: mip_bias.unwrap_or(&MipBias(0.0)).0, frustum, }), diff --git a/src/render/image/extract.rs b/src/render/image/extract.rs index 51942bfb..54f705fe 100644 --- a/src/render/image/extract.rs +++ b/src/render/image/extract.rs @@ -2,7 +2,7 @@ use crate::{ render::unified::pipeline::{ExtractedQuad, UIQuadType}, styles::Corner, }; -use bevy::{math::Vec2, prelude::*, render::color::Color}; +use bevy::{color::Color, math::Vec2, prelude::*}; pub fn extract_images( camera_entity: Entity, diff --git a/src/render/material/pipeline.rs b/src/render/material/pipeline.rs index 655bf0d9..a06dddfd 100644 --- a/src/render/material/pipeline.rs +++ b/src/render/material/pipeline.rs @@ -6,12 +6,13 @@ use bevy::{ SystemParamItem, }, }, + math::FloatOrd, prelude::*, render::{ render_asset::RenderAssets, render_phase::{ - DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, SetItemPipeline, - TrackedRenderPass, + DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult, + SetItemPipeline, TrackedRenderPass, }, render_resource::{ AsBindGroupError, BindGroup, BindGroupLayout, OwnedBindingResource, PipelineCache, @@ -19,10 +20,10 @@ use bevy::{ SpecializedRenderPipelines, }, renderer::{RenderDevice, RenderQueue}, - texture::FallbackImage, + texture::{FallbackImage, GpuImage}, Extract, }, - utils::{FloatOrd, HashMap, HashSet}, + utils::{HashMap, HashSet}, }; use kayak_font::bevy::FontTextureCache; use std::hash::Hash; @@ -206,7 +207,7 @@ pub fn prepare_materials_ui( mut extracted_assets: ResMut>, mut render_materials: ResMut>, render_device: Res, - images: Res>, + images: Res>, fallback_image: Res, pipeline: Res>, ) { @@ -253,7 +254,7 @@ pub fn prepare_materials_ui( pub fn prepare_materialui( material: &M, render_device: &RenderDevice, - images: &RenderAssets, + images: &RenderAssets, fallback_image: &FallbackImage, pipeline: &MaterialUIPipeline, ) -> Result, AsBindGroupError> { @@ -301,7 +302,7 @@ impl RenderCommand

for SetMateri let Some(material2d_handle) = material2d_handle else { return RenderCommandResult::Failure; }; - let asset_id: AssetId = material2d_handle.clone_weak().into(); + let asset_id: AssetId = material2d_handle.id(); let material2d = materials.into_inner().get(&asset_id).unwrap(); pass.set_bind_group(I, &material2d.bind_group, &[]); RenderCommandResult::Success @@ -341,7 +342,7 @@ pub fn queue_material_ui_quads( mut prev_clip, prev_index, ): ( - Res>, + Res>, Res, Res, Res>, @@ -387,7 +388,7 @@ pub fn queue_material_ui_quads( let mut pipeline_id = None; for (mut quad, material_handle, material_z) in extracted_quads.iter_mut() { - let asset_id: AssetId = material_handle.clone_weak().into(); + let asset_id: AssetId = material_handle.id(); if let Some(materialui) = render_materials.get(&asset_id) { if quad.quad_type == UIQuadType::Clip { prev_clip.rect = quad.rect; @@ -470,7 +471,7 @@ pub fn queue_material_ui_quads( rect: last_clip, batch_range: Some(old_item_start..item_end), opacity_layer: last_quad.opacity_layer, - dynamic_offset: None, + dynamic_offset: PhaseItemExtraIndex::NONE, }); } else { transparent_phase.add(TransparentUI { @@ -482,7 +483,7 @@ pub fn queue_material_ui_quads( type_index: last_quad.quad_type.get_type_index(&quad_type_offsets), rect: last_clip, batch_range: Some(old_item_start..item_end), - dynamic_offset: None, + dynamic_offset: PhaseItemExtraIndex::NONE, }); } } diff --git a/src/render/material/plugin.rs b/src/render/material/plugin.rs index 42837783..311972af 100644 --- a/src/render/material/plugin.rs +++ b/src/render/material/plugin.rs @@ -2,8 +2,8 @@ use bevy::{ prelude::*, render::{ extract_component::ExtractComponentPlugin, render_asset::prepare_assets, - render_phase::AddRenderCommand, render_resource::SpecializedRenderPipelines, Render, - RenderApp, RenderSet, + render_phase::AddRenderCommand, render_resource::SpecializedRenderPipelines, + texture::GpuImage, Render, RenderApp, RenderSet, }, }; use std::hash::Hash; @@ -52,7 +52,7 @@ where ( prepare_materials_ui:: .in_set(RenderSet::Prepare) - .after(prepare_assets::), + .after(prepare_assets::), queue_material_ui_quads:: .in_set(RenderSet::Queue) .after(crate::render::unified::pipeline::queue_quads), diff --git a/src/render/mod.rs b/src/render/mod.rs index 90c0d0d4..90c2eaba 100644 --- a/src/render/mod.rs +++ b/src/render/mod.rs @@ -5,6 +5,7 @@ use bevy::{ render_asset::RenderAssets, render_graph::{RenderGraph, RenderLabel, RenderSubGraph, RunGraphOnViewNode}, render_phase::DrawFunctions, + texture::GpuImage, Extract, ExtractSchedule, Render, RenderApp, RenderSet, }, window::{PrimaryWindow, Window, WindowRef}, @@ -18,7 +19,7 @@ use crate::{ use self::{ extract::BevyKayakUIExtractPlugin, opacity_layer::OpacityLayerManager, - ui_pass::{sort_ui_phase_system, TransparentOpacityUI, TransparentUI, UIRenderPhase}, + ui_pass::{TransparentOpacityUI, TransparentUI, UIRenderPhase}, }; mod extract; @@ -70,27 +71,19 @@ impl Plugin for BevyKayakUIRenderPlugin { prepare_opacity_layers .in_set(RenderSet::Queue) .before(unified::pipeline::queue_quads), - ) - .add_systems( - Render, - ( - sort_ui_phase_system::, - sort_ui_phase_system::, - ) - .in_set(RenderSet::PhaseSort), ); // Render graph let ui_graph_2d = get_ui_graph(render_app); let ui_graph_3d = get_ui_graph(render_app); - let mut graph = render_app.world.resource_mut::(); + let mut graph = render_app.world_mut().resource_mut::(); if let Some(graph_2d) = graph.get_sub_graph_mut(bevy::core_pipeline::core_2d::graph::Core2d) { graph_2d.add_sub_graph(DrawUiGraph, ui_graph_2d); graph_2d.add_node(KayakUiPass, RunGraphOnViewNode::new(DrawUiGraph)); graph_2d.add_node_edge( - bevy::core_pipeline::core_2d::graph::Node2d::MainPass, + bevy::core_pipeline::core_2d::graph::Node2d::EndMainPass, KayakUiPass, ); graph_2d.add_node_edge( @@ -123,8 +116,8 @@ impl Plugin for BevyKayakUIRenderPlugin { } } -fn get_ui_graph(render_app: &mut App) -> RenderGraph { - let ui_pass_node = MainPassUINode::new(&mut render_app.world); +fn get_ui_graph(render_app: &mut SubApp) -> RenderGraph { + let ui_pass_node = MainPassUINode::new(render_app.world_mut()); let mut ui_graph = RenderGraph::default(); ui_graph.add_node(KayakUiPass, ui_pass_node); ui_graph @@ -170,7 +163,7 @@ pub fn extract_core_pipeline_camera_phases( fn prepare_opacity_layers( mut opacity_layers: ResMut, - gpu_images: Res>, + gpu_images: Res>, ) { for (_, layer) in opacity_layers.camera_layers.iter_mut() { layer.set_texture_views(&gpu_images); diff --git a/src/render/nine_patch/extract.rs b/src/render/nine_patch/extract.rs index 906a8d32..aae015ea 100644 --- a/src/render/nine_patch/extract.rs +++ b/src/render/nine_patch/extract.rs @@ -2,11 +2,7 @@ use crate::{ render::unified::pipeline::{ExtractedQuad, UIQuadType}, styles::{Corner, Edge}, }; -use bevy::{ - math::Vec2, - prelude::*, - render::{color::Color, texture::Image}, -}; +use bevy::{color::Color, math::Vec2, prelude::*, render::texture::Image}; pub fn extract_nine_patch( camera_entity: Entity, diff --git a/src/render/opacity_layer.rs b/src/render/opacity_layer.rs index fdf3c146..c5cfed2b 100644 --- a/src/render/opacity_layer.rs +++ b/src/render/opacity_layer.rs @@ -6,7 +6,7 @@ use bevy::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView, }, - texture::BevyDefault, + texture::{BevyDefault, GpuImage}, view::ViewTarget, }, utils::HashMap, @@ -107,7 +107,7 @@ impl OpacityCamera { }; for (size, layer_handle) in self.layers.values_mut() { if *size != new_size { - let layer_image = images.get_mut(layer_handle.clone_weak()).unwrap(); + let layer_image = images.get_mut(layer_handle.id()).unwrap(); layer_image.texture_descriptor.format = main_texture_format; layer_image.resize(new_size); *size = new_size; @@ -119,7 +119,7 @@ impl OpacityCamera { self.layers.get(&layer_id).unwrap().1.clone_weak() } - pub(crate) fn set_texture_views(&mut self, gpu_images: &RenderAssets) { + pub(crate) fn set_texture_views(&mut self, gpu_images: &RenderAssets) { for (layer, image) in self.layers.iter() { if let Some(gpu_image) = gpu_images.get(&image.1) { self.views.insert(*layer, gpu_image.texture_view.clone()); diff --git a/src/render/texture_atlas/extract.rs b/src/render/texture_atlas/extract.rs index eef0f7c8..0aeb40f4 100644 --- a/src/render/texture_atlas/extract.rs +++ b/src/render/texture_atlas/extract.rs @@ -2,16 +2,12 @@ use crate::{ render::unified::pipeline::{ExtractedQuad, UIQuadType}, styles::Corner, }; -use bevy::{ - math::Vec2, - prelude::*, - render::{color::Color, texture::Image}, -}; +use bevy::{color::Color, math::Vec2, prelude::*, render::texture::Image}; pub fn extract_texture_atlas( camera_entity: Entity, - size: Vec2, - position: Vec2, + size: UVec2, + position: UVec2, layout: crate::layout::Rect, handle: Handle, opacity_layer: u32, @@ -42,12 +38,12 @@ pub fn extract_texture_atlas( max: Vec2::new(layout.posx + layout.width, layout.posy + layout.height), }, uv_min: Some(Vec2::new( - position.x / image_size.x, - 1.0 - ((position.y + size.y) / image_size.y), + position.x as f32 / image_size.x, + 1.0 - ((position.y + size.y) as f32 / image_size.y), )), uv_max: Some(Vec2::new( - (position.x + size.x) / image_size.x, - 1.0 - (position.y / image_size.y), + (position.x + size.x) as f32 / image_size.x, + 1.0 - (position.y as f32 / image_size.y), )), color: Color::WHITE, char_id: 0, diff --git a/src/render/ui_pass.rs b/src/render/ui_pass.rs index a75221ca..d35836e4 100644 --- a/src/render/ui_pass.rs +++ b/src/render/ui_pass.rs @@ -1,20 +1,21 @@ use std::ops::Range; use bevy::ecs::prelude::*; -use bevy::prelude::{Color, Image}; +use bevy::math::FloatOrd; +use bevy::prelude::Color; use bevy::render::render_asset::RenderAssets; use bevy::render::render_phase::{ - CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, TrackedRenderPass, + CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, PhaseItemExtraIndex, + TrackedRenderPass, }; use bevy::render::render_resource::{CachedRenderPipelineId, RenderPassColorAttachment}; +use bevy::render::texture::GpuImage; use bevy::render::{ render_graph::{Node, NodeRunError, RenderGraphContext}, render_resource::{LoadOp, Operations, RenderPassDescriptor}, renderer::RenderContext, view::{ExtractedView, ViewTarget}, }; -use bevy::utils::nonmax::NonMaxU32; -use bevy::utils::FloatOrd; use crate::CameraUIKayak; @@ -38,7 +39,7 @@ pub struct TransparentUI { pub rect: bevy::math::Rect, pub type_index: u32, pub batch_range: Option>, - pub dynamic_offset: Option, + pub dynamic_offset: PhaseItemExtraIndex, } impl TransparentUIGeneric for TransparentUI { @@ -60,13 +61,6 @@ impl TransparentUIGeneric for TransparentUI { } 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 @@ -84,12 +78,12 @@ impl PhaseItem for TransparentUI { self.batch_range.as_mut().unwrap() } - fn dynamic_offset(&self) -> Option { + fn extra_index(&self) -> PhaseItemExtraIndex { self.dynamic_offset } - fn dynamic_offset_mut(&mut self) -> &mut Option { - &mut self.dynamic_offset + fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range, &mut PhaseItemExtraIndex) { + (self.batch_range.as_mut().unwrap(), &mut self.dynamic_offset) } } @@ -111,7 +105,7 @@ pub struct TransparentOpacityUI { pub type_index: u32, pub batch_range: Option>, pub opacity_layer: u32, - pub dynamic_offset: Option, + pub dynamic_offset: PhaseItemExtraIndex, } impl TransparentUIGeneric for TransparentOpacityUI { @@ -133,13 +127,6 @@ impl TransparentUIGeneric for TransparentOpacityUI { } impl PhaseItem for TransparentOpacityUI { - type SortKey = FloatOrd; - - #[inline] - fn sort_key(&self) -> Self::SortKey { - self.sort_key - } - #[inline] fn draw_function(&self) -> DrawFunctionId { self.draw_function @@ -157,12 +144,12 @@ impl PhaseItem for TransparentOpacityUI { self.batch_range.as_mut().unwrap() } - fn dynamic_offset(&self) -> Option { + fn extra_index(&self) -> PhaseItemExtraIndex { self.dynamic_offset } - fn dynamic_offset_mut(&mut self) -> &mut Option { - &mut self.dynamic_offset + fn batch_range_and_extra_index_mut(&mut self) -> (&mut Range, &mut PhaseItemExtraIndex) { + (self.batch_range.as_mut().unwrap(), &mut self.dynamic_offset) } } @@ -225,7 +212,7 @@ impl Node for MainPassUINode { for layer_id in 1..MAX_OPACITY_LAYERS { // Start new render pass. - let gpu_images = world.get_resource::>().unwrap(); + let gpu_images = world.get_resource::>().unwrap(); let image_handle = opacity_layer_manager.get_image_handle(layer_id); let gpu_image = gpu_images.get(&image_handle).unwrap(); let pass_descriptor = RenderPassDescriptor { @@ -234,7 +221,9 @@ impl Node for MainPassUINode { view: &gpu_image.texture_view, resolve_target: None, ops: Operations { - load: LoadOp::Clear(Color::rgba(0.0, 0.0, 0.0, 0.0).into()), + load: LoadOp::Clear( + Color::srgba(0.0, 0.0, 0.0, 0.0).to_linear().into(), + ), store: bevy::render::render_resource::StoreOp::Store, }, })], @@ -303,11 +292,6 @@ impl UIRenderPhase { self.items.push(item); } - /// Sorts all of its [`PhaseItem`]s. - pub fn sort(&mut self) { - I::sort(&mut self.items); - } - /// An [`Iterator`] through the associated [`Entity`] for each [`PhaseItem`] in order. #[inline] pub fn iter_entities(&'_ self) -> impl Iterator + '_ { @@ -350,12 +334,3 @@ impl UIRenderPhase { } } } - -/// This system sorts the [`PhaseItem`]s of all [`RenderPhase`]s of this type. -pub fn sort_ui_phase_system( - mut render_phases: Query<&mut UIRenderPhase>, -) { - for mut phase in &mut render_phases { - phase.sort(); - } -} diff --git a/src/render/unified/pipeline.rs b/src/render/unified/pipeline.rs index 840c5d34..9d70450b 100644 --- a/src/render/unified/pipeline.rs +++ b/src/render/unified/pipeline.rs @@ -1,5 +1,7 @@ +use bevy::color::{Color, ColorToComponents}; use bevy::ecs::query::ROQueryItem; use bevy::ecs::system::{SystemParam, SystemParamItem}; +use bevy::math::{FloatOrd, UVec2}; use bevy::prelude::{Commands, Rect, Resource, With}; #[cfg(feature = "svg")] use bevy::prelude::{Mesh, Vec3}; @@ -7,20 +9,19 @@ use bevy::render::globals::{GlobalsBuffer, GlobalsUniform}; #[cfg(feature = "svg")] use bevy::render::mesh::VertexAttributeValues; use bevy::render::render_phase::{ - DrawFunctionId, PhaseItem, RenderCommand, RenderCommandResult, SetItemPipeline, + DrawFunctionId, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult, + SetItemPipeline, }; use bevy::render::render_resource::{ CachedRenderPipelineId, DynamicUniformBuffer, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines, }; use bevy::render::view::ViewTarget; -use bevy::utils::FloatOrd; use bevy::{ ecs::system::lifetimeless::{Read, SRes}, math::{Mat4, Quat, Vec2, Vec4}, prelude::{Component, Entity, FromWorld, Handle, Query, Res, ResMut, World}, render::{ - color::Color, render_asset::RenderAssets, render_phase::{DrawFunctions, TrackedRenderPass}, render_resource::{ @@ -90,7 +91,6 @@ pub struct UnifiedPipelineKey { impl FromWorld for UnifiedPipeline { fn from_world(world: &mut World) -> Self { - let world = world.cell(); let render_device = world.resource::(); let view_layout = render_device.create_bind_group_layout( @@ -211,7 +211,7 @@ impl FromWorld for UnifiedPipeline { sampler, texture_view, mip_level_count: 1, - size: Vec2::new(1.0, 1.0), + size: UVec2::ONE, texture_format: TextureFormat::Rgba8UnormSrgb, }; @@ -413,7 +413,7 @@ impl Default for ExtractedQuad { } #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Copy, ShaderType, Clone)] pub struct QuadVertex { pub position: [f32; 3], pub color: [f32; 4], @@ -690,7 +690,7 @@ pub struct QuadBatch { pub struct ImageBindGroups { values: HashMap, BindGroup>, font_values: HashMap, BindGroup>, - previous_sizes: HashMap, Vec2>, + previous_sizes: HashMap, UVec2>, } #[derive(Component, Debug)] @@ -843,7 +843,7 @@ pub struct QueueQuads<'w, 's> { >, image_bind_groups: ResMut<'w, ImageBindGroups>, unified_pipeline: Res<'w, UnifiedPipeline>, - gpu_images: Res<'w, RenderAssets>, + gpu_images: Res<'w, RenderAssets>, font_texture_cache: Res<'w, FontTextureCache>, quad_type_offsets: Res<'w, QuadTypeOffsets>, prev_clip: ResMut<'w, PreviousClip>, @@ -999,7 +999,7 @@ pub fn queue_quads(queue_quads: QueueQuads) { rect: last_clip, batch_range: Some(old_item_start..item_end), opacity_layer: last_quad.opacity_layer, - dynamic_offset: None, + dynamic_offset: PhaseItemExtraIndex::NONE, }); } else { transparent_phase.add(TransparentUI { @@ -1011,7 +1011,7 @@ pub fn queue_quads(queue_quads: QueueQuads) { type_index: last_quad.quad_type.get_type_index(&quad_type_offsets), rect: last_clip, batch_range: Some(old_item_start..item_end), - dynamic_offset: None, + dynamic_offset: PhaseItemExtraIndex::NONE, }); } } @@ -1031,7 +1031,7 @@ pub fn queue_quads_inner( font_texture_cache: &FontTextureCache, opacity_layers: &OpacityLayerManager, image_bind_groups: &mut ImageBindGroups, - gpu_images: &RenderAssets, + gpu_images: &RenderAssets, unified_pipeline: &UnifiedPipeline, #[cfg(feature = "svg")] render_svgs: &RenderSvgs, transparent_phase: &mut UIRenderPhase, @@ -1115,7 +1115,7 @@ pub fn queue_quads_inner( rect: *current_clip, batch_range: Some(*old_item_start..*item_end), opacity_layer: old_quad.opacity_layer, - dynamic_offset: None, + dynamic_offset: PhaseItemExtraIndex::NONE, }); } else { transparent_phase.add(TransparentUI { @@ -1127,7 +1127,7 @@ pub fn queue_quads_inner( type_index: current_batch.type_id, rect: *last_clip, batch_range: Some(*old_item_start..*item_end), - dynamic_offset: None, + dynamic_offset: PhaseItemExtraIndex::NONE, }); } @@ -1346,7 +1346,7 @@ pub fn queue_quads_inner( return; } - let color = quad.color.as_linear_rgba_f32(); + let color = quad.color.to_linear().to_f32_array(); let uv_min = quad.uv_min.unwrap_or(Vec2::ZERO); let uv_max = quad.uv_max.unwrap_or(Vec2::ONE); diff --git a/src/render/unified/shaders/bindings.wgsl b/src/render/unified/shaders/bindings.wgsl index 8ce2fb89..d8250cec 100644 --- a/src/render/unified/shaders/bindings.wgsl +++ b/src/render/unified/shaders/bindings.wgsl @@ -1,8 +1,25 @@ #define_import_path kayak_ui::bindings -#import bevy_render::view::View #import bevy_render::globals::Globals +struct View { + view_proj: mat4x4, + unjittered_view_proj: mat4x4, + inverse_view_proj: mat4x4, + view: mat4x4, + inverse_view: mat4x4, + projection: mat4x4, + inverse_projection: mat4x4, + world_position: vec3, + exposure: f32, + // viewport(x_origin, y_origin, width, height) + viewport: vec4, + frustum: array, 6>, + // color_grading: ColorGrading, + mip_bias: f32, + render_layers: u32, +}; + @group(0) @binding(0) var view: View; diff --git a/src/render/unified/text.rs b/src/render/unified/text.rs index 71f3301f..3185e72b 100644 --- a/src/render/unified/text.rs +++ b/src/render/unified/text.rs @@ -3,7 +3,7 @@ use bevy::{ render::{ render_asset::RenderAssets, renderer::{RenderDevice, RenderQueue}, - texture::Image, + texture::GpuImage, Render, RenderApp, RenderSet, }, }; @@ -27,7 +27,7 @@ fn create_and_update_font_cache_texture( device: Res, queue: Res, mut font_texture_cache: ResMut, - images: Res>, + images: Res>, ) { font_texture_cache.process_new(&device, &queue, &images); } diff --git a/src/styles/mod.rs b/src/styles/mod.rs index 33c00c0b..2e671a11 100644 --- a/src/styles/mod.rs +++ b/src/styles/mod.rs @@ -1,4 +1,5 @@ use bevy::{ + color::Srgba, prelude::{Color, Component, Vec2}, reflect::Reflect, }; @@ -92,7 +93,7 @@ impl BoxShadow { let color = if is_rgba(color) { parse_rgba(color) } else { - Color::hex(color).unwrap_or_default() + Srgba::hex(color).map(|c| c.into()).unwrap_or_default() }; box_shadows.push(BoxShadow { @@ -132,7 +133,7 @@ fn parse_rgba(s: &str) -> Color { .map(|s| s.trim().parse::().unwrap_or(1.0)) .unwrap_or(1.0); - Color::rgba(r, g, b, a) + Color::srgba(r, g, b, a) } mod tests { diff --git a/src/styles/render_command.rs b/src/styles/render_command.rs index 4b03efc0..145df08d 100644 --- a/src/styles/render_command.rs +++ b/src/styles/render_command.rs @@ -1,5 +1,6 @@ use bevy::{ - prelude::{Handle, Image, Vec2}, + math::UVec2, + prelude::{Handle, Image}, reflect::Reflect, }; #[cfg(feature = "svg")] @@ -27,8 +28,8 @@ pub enum RenderCommand { handle: Handle, }, TextureAtlas { - position: Vec2, - size: Vec2, + position: UVec2, + size: UVec2, handle: Handle, }, NinePatch { diff --git a/src/styles/style.rs b/src/styles/style.rs index 0fa75dc9..4ea73629 100644 --- a/src/styles/style.rs +++ b/src/styles/style.rs @@ -4,12 +4,14 @@ use std::ops::Add; pub use super::units::{KPositionType, LayoutType, Units}; use super::BoxShadow; +use bevy::color::Alpha; +use bevy::color::ColorToComponents; +use bevy::color::Hsva; +use bevy::color::Lcha; use bevy::prelude::Color; use bevy::prelude::Component; use bevy::prelude::ReflectComponent; use bevy::prelude::Vec2; -use bevy::prelude::Vec3; -use bevy::prelude::Vec4; use bevy::reflect::FromReflect; use bevy::reflect::Reflect; use bevy::window::CursorIcon; @@ -677,8 +679,8 @@ fn lerp_ang(a: f32, b: f32, x: f32) -> f32 { /// Linear interpolation between two colors in Lch space fn lerp_lch(a: Color, b: Color, x: f32) -> Color { - let [a_r, a_g, a_b, a_a] = a.as_lcha_f32(); - let [b_r, b_g, b_b, b_a] = b.as_lcha_f32(); + let [a_r, a_g, a_b, a_a] = Lcha::from(a).to_f32_array(); + let [b_r, b_g, b_b, b_a] = Lcha::from(b).to_f32_array(); let hue = lerp_ang(a_b, b_b, x); let a_xy = Vec2::new(a_r, a_g); @@ -687,116 +689,26 @@ fn lerp_lch(a: Color, b: Color, x: f32) -> Color { let alpha = lerp(a_a, b_a, x); - Color::Lcha { + Color::Lcha(Lcha { lightness: xy.x, chroma: xy.y, hue, alpha, - } - .as_rgba() -} - -fn rgb_to_hsv(from: &Color) -> Vec3 { - // xyz <-> hsv - let r = from.r(); - let g = from.g(); - let b = from.b(); - - let mut res = Vec3::ZERO; - - let min = r.min(g).min(b); - let max = r.max(g).max(b); - - // Value - res.z = max; - - let delta = max - min; - // calc Saturation - if max != 0.0 { - res.y = delta / max; - } else { - res.x = -1.0; - res.y = 0.0; - - return res; - } - - // calc Hue - if r == max { - // between Yellow & Magenta - res.x = (g - b) / delta; - } else if g == max { - // cyan to yellow - res.x = 2.0 + (b - r) / delta; - } else { - // b == max // Megnta to cyan - res.x = 4.0 + (r - g) / delta; - } - - res.x *= 60.0; // Convert to degrees - if res.x < 0.0 { - res.x += 360.0; // Unwrap angle in case of negative - } - - res -} - -fn hsv_to_rgb(from: &Vec3) -> Color { - let h = from.x; - let s = from.y; - let v = from.z; - - // Calc base values - let c = s * v; - let x = c * (1.0 - (((h / 60.0) % 2.0) - 1.0).abs()); - let m = v - c; - - let mut res = Vec4::new(0.0, 0.0, 0.0, 1.0); - - if (0.0..60.0).contains(&h) { - res.x = c; - res.y = x; - res.z = 0.0; - } else if (60.0..120.0).contains(&h) { - res.x = x; - res.y = c; - res.z = 0.0; - } else if (120.0..180.0).contains(&h) { - res.x = 0.0; - res.y = c; - res.z = x; - } else if (180.0..240.0).contains(&h) { - res.x = 0.0; - res.y = x; - res.z = c; - } else if (240.0..300.0).contains(&h) { - res.x = x; - res.y = 0.0; - res.z = c; - } else { - res.x = c; - res.y = 0.0; - res.z = x; - } - - res += Vec4::new(m, m, m, 0.0); - - Color::rgba_from_array(res) + }) } fn hsv_lerp(from: &Color, to: &Color, amount: f32) -> Color { - let from_a = from.a(); - let to_a = to.a(); - let from = rgb_to_hsv(from); - let to = rgb_to_hsv(to); - let mut res = from.lerp(to, amount); - - if from.x < 0.0 { - res.x = to.x; + let from_a = from.alpha(); + let to_a = to.alpha(); + let from = Hsva::from(*from); + let to = Hsva::from(*to); + let mut res = Hsva::from_vec4(from.to_vec4().lerp(to.to_vec4(), amount)); + + if from.hue < 0.0 { + res.hue = to.hue; } - let mut color = hsv_to_rgb(&res); - color.set_a(lerp(from_a, to_a, amount).clamp(0.0, 1.0)); - color + res.set_alpha(lerp(from_a, to_a, amount).clamp(0.0, 1.0)); + Color::from(res) } pub(crate) fn lerp(a: f32, b: f32, x: f32) -> f32 { diff --git a/src/widgets/button.rs b/src/widgets/button.rs index a804bf3d..da6c7358 100644 --- a/src/widgets/button.rs +++ b/src/widgets/button.rs @@ -56,7 +56,7 @@ pub fn button_render( state_query: Query<&ButtonState>, ) -> bool { if let Ok((button, styles, mut computed_styles)) = query.get_mut(entity) { - let hover_color = Color::rgba(0.592, 0.627, 0.749, 1.0); //Color::rgba(0.549, 0.666, 0.933, 1.0); + let hover_color = Color::srgba(0.592, 0.627, 0.749, 1.0); //Color::rgba(0.549, 0.666, 0.933, 1.0); let font_size = styles.font_size.resolve_or(16.); let height = styles.height.resolve_or(Units::Pixels(28.)); @@ -72,11 +72,11 @@ pub fn button_render( }) .with_style(styles) .with_style(KStyle { - background_color: Color::rgba(0.254, 0.270, 0.349, 1.0).into(), + background_color: Color::srgba(0.254, 0.270, 0.349, 1.0).into(), border_color: if state.hovering { hover_color.into() } else { - Color::rgba(0.254, 0.270, 0.349, 1.0).into() + Color::srgba(0.254, 0.270, 0.349, 1.0).into() }, border: Edge::all(2.0).into(), border_radius: StyleProp::Value(Corner::all(10.0)), diff --git a/src/widgets/modal.rs b/src/widgets/modal.rs index 96973893..5f1111e6 100644 --- a/src/widgets/modal.rs +++ b/src/widgets/modal.rs @@ -137,14 +137,14 @@ pub fn render( >, font_mapping: Res, mut state_query: Query<&mut TextBoxState>| { - match event.event_type { + match &event.event_type { EventType::KeyDown(key_event) => { if key_event.key() == KeyCode::ArrowRight { if let Ok(mut state) = state_query.get_mut(state_entity) { @@ -215,15 +215,34 @@ pub fn text_box_render( } } } - EventType::CharInput { ref c } => { + EventType::KeyUp(e) => { if let Ok(mut state) = state_query.get_mut(state_entity) { let cloned_on_change = cloned_on_change.clone(); if !state.focused { return; } let cursor_pos = state.cursor_position; - for c in c.chars() { - if is_backspace(c) { + match e.logical_key() { + Key::Character(c) => { + let char_pos: usize = state.graphemes[0..cursor_pos] + .iter() + .map(|g| g.len()) + .sum(); + for c in c.chars() { + state.current_value.insert(char_pos, c); + } + + state.cursor_position += 1; + } + Key::Space => { + let char_pos: usize = state.graphemes[0..cursor_pos] + .iter() + .map(|g| g.len()) + .sum(); + state.current_value.insert(char_pos, ' '); + state.cursor_position += 1; + } + Key::Backspace => { if !state.current_value.is_empty() { let char_pos: usize = state.graphemes [0..cursor_pos - 1] @@ -233,15 +252,8 @@ pub fn text_box_render( state.current_value.remove(char_pos); state.cursor_position -= 1; } - } else if !c.is_control() { - let char_pos: usize = state.graphemes[0..cursor_pos] - .iter() - .map(|g| g.len()) - .sum(); - state.current_value.insert(char_pos, c); - - state.cursor_position += 1; } + _ => {} } // Update graphemes @@ -284,7 +296,7 @@ pub fn text_box_render( ); let cursor_styles = KStyle { - background_color: Color::rgba(0.933, 0.745, 0.745, 1.0).into(), + background_color: Color::srgba(0.933, 0.745, 0.745, 1.0).into(), position_type: KPositionType::SelfDirected.into(), top: Units::Pixels(5.0).into(), left: Units::Pixels(state.cursor_x).into(), diff --git a/src/widgets/texture_atlas.rs b/src/widgets/texture_atlas.rs index 56dfd1c7..1585ca31 100644 --- a/src/widgets/texture_atlas.rs +++ b/src/widgets/texture_atlas.rs @@ -1,4 +1,7 @@ -use bevy::prelude::{Bundle, Component, Entity, Handle, Image, In, Query, Vec2}; +use bevy::{ + math::UVec2, + prelude::{Bundle, Component, Entity, Handle, Image, In, Query}, +}; use crate::{ context::WidgetName, @@ -26,9 +29,9 @@ pub struct TextureAtlasProps { /// The handle to image pub handle: Handle, /// The position of the tile (in pixels) - pub position: Vec2, + pub position: UVec2, /// The size of the tile (in pixels) - pub tile_size: Vec2, + pub tile_size: UVec2, } impl Widget for TextureAtlasProps {} diff --git a/src/widgets/window.rs b/src/widgets/window.rs index 731762a3..b8190e3c 100644 --- a/src/widgets/window.rs +++ b/src/widgets/window.rs @@ -162,8 +162,8 @@ pub fn window_render(