diff --git a/Cargo.toml b/Cargo.toml index b7266f9c4c41f..f4e0d88af7e22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -912,6 +912,7 @@ wasm = true name = "2d_shapes" path = "examples/2d/2d_shapes.rs" doc-scrape-examples = true +required-features = ["bevy_feathers"] [package.metadata.example.2d_shapes] name = "2D Shapes" diff --git a/examples/2d/2d_shapes.rs b/examples/2d/2d_shapes.rs index 844c7778eceb2..d750dad0dc560 100644 --- a/examples/2d/2d_shapes.rs +++ b/examples/2d/2d_shapes.rs @@ -1,5 +1,4 @@ //! Here we use shape primitives to build meshes in a 2D rendering context, making each mesh a certain color by giving that mesh's entity a material based off a [`Color`]. -//! //! Meshes are better known for their use in 3D rendering, but we can use them in a 2D context too. Without a third dimension, the meshes we're building are flat – like paper on a table. These are still very useful for "vector-style" graphics, picking behavior, or as a foundation to build off of for where to apply a shader. //! //! A "shape definition" is not a mesh on its own. A circle can be defined with a radius, i.e. [`Circle::new(50.0)`][Circle::new], but rendering tends to happen with meshes built out of triangles. So we need to turn shape descriptions into meshes. @@ -10,33 +9,55 @@ //! //! Both the mesh and material need to be wrapped in their own "newtypes". The mesh and material are currently [`Handle`] and [`Handle`] at the moment, which are not components. Handles are put behind "newtypes" to prevent ambiguity, as some entities might want to have handles to meshes (or images, or materials etc.) for different purposes! All we need to do to make them rendering-relevant components is wrap the mesh handle and the material handle in [`Mesh2d`] and [`MeshMaterial2d`] respectively. //! -//! You can toggle wireframes with the space bar except on wasm. Wasm does not support +//! You can toggle wireframes with a Feathers checkbox except on wasm. Wasm does not support //! `POLYGON_MODE_LINE` on the gpu. +use bevy::prelude::*; #[cfg(not(target_arch = "wasm32"))] +use bevy::sprite_render::{Wireframe2dConfig, Wireframe2dPlugin}; use bevy::{ - input::common_conditions::input_just_pressed, - sprite_render::{Wireframe2dConfig, Wireframe2dPlugin}, + feathers::{controls::FeathersCheckbox, theme::UiTheme, FeathersPlugins}, + ui_widgets::{checkbox_self_update, ValueChange}, }; -use bevy::{input::common_conditions::input_toggle_active, prelude::*}; +use checkbox::feathers_option_checkbox; +use scene::{bottom_left_scene, top_left_scene}; + +#[path = "../helpers/checkbox.rs"] +mod checkbox; + +#[path = "../helpers/theme.rs"] +mod theme; + +#[path = "../helpers/scene.rs"] +mod scene; + +/// Various settings for the demo. +#[derive(Resource, Default)] +struct AppStatus { + rotation: bool, +} + +#[derive(Clone, Copy, Component, Debug, Default, PartialEq)] +enum CheckboxInput { + #[default] + Rotation, + Wireframe, +} fn main() { let mut app = App::new(); app.add_plugins(( DefaultPlugins, + FeathersPlugins, #[cfg(not(target_arch = "wasm32"))] Wireframe2dPlugin::default(), )) + .init_resource::() .add_systems(Startup, setup); - #[cfg(not(target_arch = "wasm32"))] - app.add_systems( - Update, - toggle_wireframe.run_if(input_just_pressed(KeyCode::Space)), - ); - app.add_systems( - Update, - rotate.run_if(input_toggle_active(false, KeyCode::KeyR)), - ); + app.insert_resource(UiTheme(theme::basic_example_theme(Color::WHITE))); + app.add_observer(handle_value_change_checkbox); + app.add_systems(Update, rotate.run_if(rotate_checkbox_checked)); + app.add_observer(checkbox_self_update); app.run(); } @@ -142,25 +163,52 @@ fn setup( ), )); } + spawn_buttons(&mut commands); +} - let mut text = "Press 'R' to pause/resume rotation".to_string(); - #[cfg(not(target_arch = "wasm32"))] - text.push_str("\nPress 'Space' to toggle wireframes"); - - commands.spawn(( - Text::new(text), - Node { - position_type: PositionType::Absolute, - top: px(12), - left: px(12), - ..default() - }, - )); +/// Spawns the checkboxes in the bottom left corner of the screen. +fn spawn_buttons(commands: &mut Commands) { + if !cfg!(target_arch = "wasm32") { + commands.spawn_scene(bsn! { + bottom_left_scene() + Children [ + feathers_option_checkbox("ROTATE", Some(CheckboxInput::Rotation)), + feathers_option_checkbox("WIREFRAME", Some(CheckboxInput::Wireframe)), + ] + }); + } else { + commands.spawn_scene(bsn! { + top_left_scene() // so the user can immediately see the control in browser w/o scrolling + Children [ + feathers_option_checkbox("ROTATE", Some(CheckboxInput::Rotation)), + ] + }); + } } -#[cfg(not(target_arch = "wasm32"))] -fn toggle_wireframe(mut wireframe_config: ResMut) { - wireframe_config.global = !wireframe_config.global; +fn handle_value_change_checkbox( + event: On>, + #[cfg(not(target_arch = "wasm32"))] mut wireframe_config: ResMut, + mut app_status: ResMut, + checkbox_input_q: Query<&CheckboxInput, With>, +) { + if let Ok(checkbox_input) = checkbox_input_q.get(event.source) { + match checkbox_input { + CheckboxInput::Rotation => { + app_status.rotation = event.value; + } + #[cfg(target_arch = "wasm32")] + CheckboxInput::Wireframe => {} + #[cfg(not(target_arch = "wasm32"))] + CheckboxInput::Wireframe => { + wireframe_config.global = event.value; + } + } + } +} + +fn rotate_checkbox_checked(app_status: Res) -> bool { + app_status.rotation } fn rotate(mut query: Query<&mut Transform, With>, time: Res