From c349ce08d038b8013e849b5a57a6edb169a3c447 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Fri, 18 Jul 2025 11:32:00 -0700 Subject: [PATCH] Starting on ui example --- Cargo.toml | 3 +++ build.rs | 14 ++++++++---- examples/ui.rs | 53 +++++++++++++++++++++++++++++++++++++++++++ flake.lock | 6 ++--- rust-toolchain.toml | 2 +- src/base_game.rs | 1 + src/bin/trees/main.rs | 24 ++++++++++---------- src/bin/trees/mono.rs | 8 +++---- src/ui.rs | 8 +++++++ 9 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 examples/ui.rs diff --git a/Cargo.toml b/Cargo.toml index 791a326..6e4a8b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,6 @@ features = ["wayland", "dynamic_linking"] [build-dependencies] chrono = "*" walkdir = "*" + +# [hints] +# mostly-unused = true diff --git a/build.rs b/build.rs index f40b036..ac7d01f 100644 --- a/build.rs +++ b/build.rs @@ -50,10 +50,16 @@ fn write_monologues_file() { walkdir::WalkDir::new("assets/trees") .into_iter() .filter_map(|entry| entry.ok()) - .filter(|entry| { - entry.path().extension() == Some(std::ffi::OsStr::new("mono")) - }) + .filter(|entry| entry.path().extension() == Some(std::ffi::OsStr::new("mono"))) .for_each(|entry| { - let _ = writeln!(file, "{}", entry.path().to_string_lossy().strip_prefix("assets/").unwrap()); + let _ = writeln!( + file, + "{}", + entry + .path() + .to_string_lossy() + .strip_prefix("assets/") + .unwrap() + ); }); } diff --git a/examples/ui.rs b/examples/ui.rs new file mode 100644 index 0000000..7412578 --- /dev/null +++ b/examples/ui.rs @@ -0,0 +1,53 @@ +//! This example illustrates scrolling in Bevy UI. + +use games::*; + +fn main() { + let mut app = App::new(); + app.add_plugins(BaseGamePlugin::default()) + .add_systems(Startup, setup); + app.run(); +} + +const LINE_HEIGHT: f32 = 21.; + +fn setup(mut commands: Commands) { + // Scrolling list + commands + .spawn(( + Node { + flex_direction: FlexDirection::Column, + // If height is not set, we need both align_self: Stetch and overflow: scroll() + height: Val::Percent(50.0), + // align_self: AlignSelf::Stretch, + overflow: Overflow::scroll(), + ..default() + }, + BackgroundColor(Color::srgb(0.10, 0.10, 0.10)), + )) + .with_children(|parent| { + // List items + (0..250).for_each(|i| { + parent.spawn(Text(format!("Item {i}"))); + }); + }) + .observe(scroll); +} + +/// Updates the scroll position of scrollable nodes in response to mouse input +pub fn scroll( + trigger: Trigger>, + mut scrollers: Query<&mut ScrollPosition>, +) { + let Pointer { event: Scroll { unit, x, y, .. }, .. } = trigger.event(); + + let (dx, dy) = match unit { + MouseScrollUnit::Line => (x * LINE_HEIGHT, y * LINE_HEIGHT), + MouseScrollUnit::Pixel => (x * 1., y * 1.), + }; + + if let Ok(mut pos) = scrollers.get_mut(trigger.target()) { + pos.offset_x -= dx; + pos.offset_y -= dy; + } +} diff --git a/flake.lock b/flake.lock index 61dd58d..adf40ff 100644 --- a/flake.lock +++ b/flake.lock @@ -20,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1750506804, - "narHash": "sha256-VLFNc4egNjovYVxDGyBYTrvVCgDYgENp5bVi9fPTDYc=", + "lastModified": 1752480373, + "narHash": "sha256-JHQbm+OcGp32wAsXTE/FLYGNpb+4GLi5oTvCxwSoBOA=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "4206c4cb56751df534751b058295ea61357bbbaa", + "rev": "62e0f05ede1da0d54515d4ea8ce9c733f12d9f08", "type": "github" }, "original": { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0281a60..31ec0f4 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "nightly-2025-06-16" +channel = "nightly-2025-07-16" components = [ "rustfmt", "rustc-dev", "cargo", "clippy", "rust-analyzer", "rustc-codegen-cranelift" ] targets = [ "x86_64-unknown-linux-gnu", "wasm32-unknown-unknown" ] diff --git a/src/base_game.rs b/src/base_game.rs index 9f1132d..e7a38de 100644 --- a/src/base_game.rs +++ b/src/base_game.rs @@ -27,6 +27,7 @@ impl Plugin for BaseGamePlugin { .add_plugins(MeshPickingPlugin) .add_plugins(RapierPhysicsPlugin::::default()) .add_plugins(LoadingPlugin) + .add_plugins(BaseUiPlugin) .add_systems(Startup, setup_camera); } } diff --git a/src/bin/trees/main.rs b/src/bin/trees/main.rs index 26cfcc2..295a502 100644 --- a/src/bin/trees/main.rs +++ b/src/bin/trees/main.rs @@ -528,13 +528,11 @@ fn delete_tree(trigger: Trigger>, mut commands: Commands) { } /// Load all monologues so they are in the asset store and trigger on-load events -fn load_monologues( - server: ResMut, - mut loaded_assets: Local>>, -) { - *loaded_assets = include_str!("../../../assets/trees/MONOLOGUES").split("\n").map(|path| { - server.load(path) - }).collect(); +fn load_monologues(server: ResMut, mut loaded_assets: Local>>) { + *loaded_assets = include_str!("../../../assets/trees/MONOLOGUES") + .split("\n") + .map(|path| server.load(path)) + .collect(); } fn spawn_debug_buttons( @@ -700,6 +698,7 @@ fn hide_monologue_preview( mut commands: Commands, ) { if !query.contains(trigger.target()) { + info!("Processing over: {:?}", trigger.target()); commands.entity(*preview).despawn_related::(); } } @@ -709,17 +708,18 @@ fn drag_tree( state: Res>, mut query: Query<&mut Transform, With>, camera: Single<(&Camera, &GlobalTransform), With>, - window: Single<&Window> + window: Single<&Window>, ) { if *state.get() == DebuggingState::On { if let Ok(mut t) = query.get_mut(trigger.target()) { let world_position = window .cursor_position() - .and_then(|cursor| { - camera.0.viewport_to_world(camera.1, cursor).ok() - }).map(|ray| { + .and_then(|cursor| camera.0.viewport_to_world(camera.1, cursor).ok()) + .map(|ray| { // Compute ray's distance to entity - let distance = ray.intersect_plane(t.translation, InfinitePlane3d::new(t.up())).unwrap(); + let distance = ray + .intersect_plane(t.translation, InfinitePlane3d::new(t.up())) + .unwrap(); ray.get_point(distance) }); t.translation = world_position.unwrap(); diff --git a/src/bin/trees/mono.rs b/src/bin/trees/mono.rs index fd7c45e..8154c48 100644 --- a/src/bin/trees/mono.rs +++ b/src/bin/trees/mono.rs @@ -42,9 +42,7 @@ impl Display for MonologueLine { impl From for MonologueLine { fn from(value: String) -> Self { - MonologueLine { - value - } + MonologueLine { value } } } @@ -57,7 +55,7 @@ impl Into for MonologueLine { impl From<&str> for MonologueLine { fn from(value: &str) -> Self { MonologueLine { - value: value.into() + value: value.into(), } } } @@ -102,7 +100,7 @@ impl AssetLoader for MonologueLoader { // Skip any empty lines or comments } else if line.starts_with("#") || line.is_empty() { // Skip comments and blank lines - // everything else we read as a monologue line + // everything else we read as a monologue line } else { monologue.batches.last_mut().unwrap().add_line(line.into()); } diff --git a/src/ui.rs b/src/ui.rs index 4d1fce1..0049660 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,5 +1,13 @@ use super::*; +pub(crate) struct BaseUiPlugin; + +impl Plugin for BaseUiPlugin { + fn build(&self, app: &mut App) { + // Nope! + } +} + /// Marker component for handling Resource -> Ui Sync #[derive(Component, Default, Debug)] pub struct SyncResource(R);