diff --git a/bin/editor.rs b/bin/editor.rs index abad074..1ff76fe 100644 --- a/bin/editor.rs +++ b/bin/editor.rs @@ -5,15 +5,14 @@ // TODO: // * Tree Organization: GLTF contains Animations and Scenes // * Camera can only select one at a time. -// * (easy) Load audios like current GLTFs -// * (easy) Loop audio enable/disable // * (easy) Better Colorscheme -// * (easy) Interactive buttons (hover/click) // * (medium) Visual errors for bad GLTFs // * (medium) Collapsable containers (Gltfs, Animations, Scenes, Audio Clips, Cameras) // * (medium) Spawn clicked scene // * (medium) Play clicked animation // * (idea) Use enum instead of markers for exclusive UI +// * (medium) Add fonts similar to Audios based on inspect-fonts +// * (hard) Add Dialogs (requires text box UI, saving, loading) use bevy::{ asset::{AssetPath, Assets}, @@ -57,6 +56,7 @@ fn main() { // Audio Systems load_audio, unload_audio, + manage_audio_ui, play_audio, // Level Import/Export systems export_level, @@ -72,7 +72,8 @@ fn main() { #[derive(Debug, Component)] enum UiRef { Handle(T), - Entity(T), + // Entity(T), + // Event(T), } /// UI: @@ -135,7 +136,7 @@ fn initialize_ui(mut commands: Commands) { fn load_bogus( mut events: EventReader, - root: Query>, + root: Query, Without>>)>, mut commands: Commands, ) { events @@ -197,7 +198,7 @@ fn get_fname(asset_path: AssetPath, suffixes: &[&str]) -> String { /// This should be a separate async system fn manage_gltf_ui( mut events: EventReader>, - root: Query>, + root: Query, Without>>)>, mut commands: Commands, server: Res, ) { @@ -214,6 +215,9 @@ fn manage_gltf_ui( _ => None, }) .for_each(|(handle, name)| { + root.iter().for_each(|entity| { + commands.entity(entity).log_components(); + }); commands .spawn(( GameUiButton, @@ -236,7 +240,7 @@ struct ScenesUi; /// Sync scene assets with UI fn manage_scene_ui( mut events: EventReader>, - root: Query>, + root: Query, Without>>)>, mut commands: Commands, gltfs: Res>, registry: Res, @@ -252,10 +256,6 @@ fn manage_scene_ui( |gltf_handle| match gltfs.get(&gltf_handle.clone().typed::()) { Some(gltf) => { gltf.named_scenes.iter().find_map(|(name, scene_handle)| { - info!( - "scene_handle({:?}) == handle({:?})", - scene_handle, handle - ); (scene_handle == handle).then_some(name) }) } @@ -292,7 +292,7 @@ struct AnimationsUi; fn manage_animation_ui( mut events: EventReader>, - root: Query>, + root: Query, Without>>)>, mut commands: Commands, gltfs: Res>, registry: Res, @@ -309,10 +309,6 @@ fn manage_animation_ui( match gltfs.get(&gltf_handle.clone().typed::()) { Some(gltf) => gltf.named_animations.iter().find_map( |(name, animation_handle)| { - info!( - "animation_handle({:?}) == handle({:?})", - animation_handle, handle - ); (animation_handle == handle).then_some(name) }, ), @@ -342,7 +338,83 @@ fn manage_animation_ui( struct AudioClipsUi; /// Drag+Drop import Audio to editor -fn load_audio() {} +fn load_audio( + mut events: EventReader, + server: Res, + mut assets: ResMut, +) { + events + .iter() + .filter_map(|event| match event { + FileDragAndDrop::DroppedFile { path_buf, .. } => Some(path_buf), + _ => None, + }) + .for_each(|path_buf| { + let path = path_buf.as_path(); + let handle = server.load_untyped(path); + assets.0.insert(handle); + }); +} + +fn manage_audio_ui( + mut events: EventReader>, + root: Query, Without>>)>, + mut commands: Commands, + server: Res, +) { + events + .iter() + .filter_map(|event| match event { + AssetEvent::Created { handle } => { + let asset_path = server + .get_handle_path(handle.clone()) + .expect("Fetch Asset Path"); + let name = get_fname(asset_path, &[".ogg"]); + Some((handle.clone(), String::from(name))) + } + _ => None, + }) + .for_each(|(handle, name)| { + commands + .spawn(( + GameUiButton, + Name::new(name), + NodeBundle { ..default() }, + AudioClipsUi, + AudioBundle { + source: handle.clone(), + settings: PlaybackSettings { + mode: bevy::audio::PlaybackMode::Loop, + paused: true, + ..default() + }, + }, + )) + .set_parent(root.single()); + }); +} + +/// Play/Loop Audio +fn play_audio( + mut events: Query< + (&Interaction, &AudioSink, &mut UiElementState), + (Changed, With), + >, +) { + events + .iter_mut() + .for_each(|(interaction, sink, mut state)| match interaction { + Interaction::Pressed => { + sink.toggle(); + + *state = match *state { + UiElementState::Enabled => UiElementState::Active, + _ => UiElementState::Enabled, + } + } + _ => (), + }); +} /// Remove audio from editor fn unload_audio() {} @@ -350,9 +422,6 @@ fn unload_audio() {} /// Spawn Scene fn spawn_scene() {} -/// Play/Loop Audio -fn play_audio() {} - /// Export level fn export_level() {} diff --git a/src/ui.rs b/src/ui.rs index 28a138c..2a835ca 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -1,3 +1,5 @@ +/// TODO: Sorted list/set +/// use bevy::{prelude::*, window::PrimaryWindow}; pub struct GameUiPlugin; @@ -11,11 +13,21 @@ impl Plugin for GameUiPlugin { manage_ui_set, manage_ui_button, manage_cursor, + manage_button_interaction, ), ); } } +/// Describes the state of an element +#[derive(Debug, Component)] +pub enum UiElementState { + Enabled, + Disabled, + Active, + Error, +} + /// GameUiList for holding ordered collections of objects #[derive(Debug, Component)] pub struct GameUiList; @@ -58,7 +70,7 @@ fn manage_ui_set(events: Query<(Entity, &Name), Added>, mut commands: .entity(entity) .insert(NodeBundle { style: Style { - // width: Val::Px(100.0), + width: Val::Px(200.0), margin: UiRect::all(Val::Px(2.0)), padding: UiRect::all(Val::Px(2.0)), border: UiRect::all(Val::Px(2.0)), @@ -87,18 +99,21 @@ fn manage_ui_button(events: Query<(Entity, &Name), Added>, mut com events.iter().for_each(|(entity, name)| { commands .entity(entity) - .insert(ButtonBundle { - style: Style { - margin: UiRect::all(Val::Px(2.0)), - padding: UiRect::all(Val::Px(2.0)), - border: UiRect::all(Val::Px(2.0)), - justify_content: JustifyContent::Center, + .insert(( + ButtonBundle { + style: Style { + margin: UiRect::all(Val::Px(2.0)), + padding: UiRect::all(Val::Px(2.0)), + border: UiRect::all(Val::Px(2.0)), + justify_content: JustifyContent::Center, + ..default() + }, + background_color: BackgroundColor(Color::GREEN), + border_color: BorderColor(Color::BLACK), ..default() }, - background_color: BackgroundColor(Color::GREEN), - border_color: BorderColor(Color::BLACK), - ..default() - }) + UiElementState::Enabled, + )) .with_children(|parent| { parent.spawn(TextBundle::from_section(name, TextStyle::default())); }); @@ -108,14 +123,38 @@ fn manage_ui_button(events: Query<(Entity, &Name), Added>, mut com /// Manage the cursor icon for better immersion fn manage_cursor( mut primary_window: Query<&mut Window, With>, - events: Query<&Interaction, With>, + events: Query<&Interaction, (Changed, With