You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

179 lines
5.6 KiB
Rust

use bevy::prelude::*;
use monologue_trees::debug::*;
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "Audio Inspect".into(),
resolution: (640., 480.).into(),
..default()
}),
..default()
}),
DebugInfoPlugin,
))
.add_systems(PreStartup, (load,))
.add_systems(Startup, (init,))
.add_systems(Update, (save, update, sort_buttons))
.run()
}
/// Stores audio handles so they don't get dropped
#[derive(Component, Debug)]
struct AudioItem(Handle<AudioSource>);
#[derive(Component)]
struct Container;
///
/// Load audio assets
/// TODO: Does this load new items when they're added to the folder?
fn load(server: Res<AssetServer>) {
// To prevent handles from being dropped,
// they are stored on entities created triggered on AssetEvent<AudioSource>::Created
let _yeet = server.load_folder("audio").expect("Load audios");
}
///
/// Initialize audio inspector UI
fn init(mut commands: Commands) {
commands.spawn(Camera3dBundle { ..default() });
commands.spawn((
NodeBundle {
style: Style {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
align_items: AlignItems::FlexStart,
align_content: AlignContent::FlexStart,
flex_direction: FlexDirection::Row,
flex_wrap: FlexWrap::Wrap,
..default()
},
background_color: BackgroundColor(Color::MIDNIGHT_BLUE),
..default()
},
Container,
));
}
///
/// Save loaded audio sources to entities
fn save(
mut audio_load_events: EventReader<AssetEvent<AudioSource>>,
server: Res<AssetServer>,
mut commands: Commands,
container_q: Query<Entity, With<Container>>,
) {
for event in audio_load_events.iter() {
match event {
AssetEvent::Created { handle } => {
let style = TextStyle {
color: Color::BLACK,
font_size: 16.0,
..default()
};
let handle_path = server
.get_handle_path(handle.clone())
.expect("Get handle path");
let path = handle_path.path().to_str().expect("Convert path to str");
let title = path
.split("/")
.last()
.expect("Extracting filename")
.trim_end_matches(".ogg");
commands
.spawn((
Name::new(format!("{}", title)),
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(1.0)),
margin: UiRect::all(Val::Px(2.0)),
padding: UiRect::all(Val::Px(2.0)),
..default()
},
border_color: BorderColor(Color::BLACK),
..default()
},
AudioBundle {
source: handle.clone(),
settings: PlaybackSettings {
mode: bevy::audio::PlaybackMode::Loop,
paused: true,
..default()
},
},
))
.with_children(|parent| {
parent.spawn(TextBundle::from_section(title, style));
})
.set_parent(container_q.single());
}
AssetEvent::Modified { .. } => {
debug_assert!(false, "Audio file was modified, not handled!")
}
AssetEvent::Removed { .. } => {
debug_assert!(false, "Audio file was deleted, not handled!")
}
}
}
}
fn sort_buttons(
mut container: Query<&mut Children, (With<Container>, Changed<Children>)>,
names: Query<&Name, With<Parent>>,
) {
container.iter_mut().for_each(|mut children| {
children.sort_by(|&a, &b| {
names
.get(a)
.expect("Button has name")
.cmp(names.get(b).expect("Button has name"))
});
});
}
/// Update loop; play/pause/volume
fn update(
mut interactions: Query<
(&Interaction, &AudioSink, &mut BackgroundColor),
(With<Button>, Changed<Interaction>),
>,
) {
interactions
.iter_mut()
.for_each(|(interaction, sink, mut bg_color)| {
// Toggle audio loop
// Toggle background color
bg_color.0 = match interaction {
Interaction::Hovered => {
if bg_color.0 == Color::ORANGE {
Color::ORANGE
} else {
Color::ORANGE_RED
}
}
Interaction::Pressed => {
sink.toggle();
if bg_color.0 == Color::ORANGE {
Color::WHITE
} else {
Color::ORANGE
}
}
Interaction::None => {
if bg_color.0 == Color::ORANGE_RED {
Color::WHITE
} else {
bg_color.0
}
}
}
})
}