Compare commits

..

No commits in common. '87a4c6fd409d15ea120bfa5f7397c7e48e9116f9' and 'e159346b8946e9bfa095dbe6a897218a7e516b68' have entirely different histories.

@ -0,0 +1,26 @@
# Exploration
## Inspectors
### Model Inspector
- [ ] Construct Scene from Nodes/Meshes (not auto-scene builder)
- [ ] Show debug info about selected model
- [ ] Wireframe view
- [ ] Automatic tighter bounding box for selection
### Audio Inspector
- [ ] UI for selecting sound
- [ ] Play/Pause/Volume
- [x] Load sounds
- [ ] Scrolling list of sounds
## WASM
- [ ] Build and run using model/text inspector
- https://github.com/bevyengine/bevy/blob/main/examples/README.md#wasm
## Text Inspector
- [ ] Performance improvements?

@ -5,15 +5,15 @@
// TODO: // TODO:
// * Tree Organization: GLTF contains Animations and Scenes // * Tree Organization: GLTF contains Animations and Scenes
// * Camera can only select one at a time. // * Camera can only select one at a time.
// * (hard) Better Colorscheme // * (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) Visual errors for bad GLTFs
// * (medium) Collapsable containers (Gltfs, Animations, Scenes, Audio Clips, Cameras) // * (medium) Collapsable containers (Gltfs, Animations, Scenes, Audio Clips, Cameras)
// * (medium) Show/hide entire UI
// * (medium) Spawn clicked scene // * (medium) Spawn clicked scene
// * (medium) Play clicked animation // * (medium) Play clicked animation
// * (idea) Use enum instead of markers for exclusive UI // * (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::{ use bevy::{
asset::{AssetPath, Assets}, asset::{AssetPath, Assets},
@ -57,7 +57,6 @@ fn main() {
// Audio Systems // Audio Systems
load_audio, load_audio,
unload_audio, unload_audio,
manage_audio_ui,
play_audio, play_audio,
// Level Import/Export systems // Level Import/Export systems
export_level, export_level,
@ -73,8 +72,7 @@ fn main() {
#[derive(Debug, Component)] #[derive(Debug, Component)]
enum UiRef<T> { enum UiRef<T> {
Handle(T), Handle(T),
// Entity(T), Entity(T),
// Event(T),
} }
/// UI: /// UI:
@ -103,13 +101,7 @@ fn initialize_ui(mut commands: Commands) {
.spawn(( .spawn((
GameUiList, GameUiList,
Name::new("GLTFs"), Name::new("GLTFs"),
NodeBundle { NodeBundle { ..default() },
style: Style {
flex_direction: FlexDirection::Column,
..default()
},
..default()
},
GltfsUi, GltfsUi,
)) ))
.with_children(|parent| { .with_children(|parent| {
@ -135,13 +127,7 @@ fn initialize_ui(mut commands: Commands) {
parent.spawn(( parent.spawn((
GameUiSet, GameUiSet,
Name::new("Audio Clips"), Name::new("Audio Clips"),
NodeBundle { NodeBundle { ..default() },
style: Style {
flex_direction: FlexDirection::Column,
..default()
},
..default()
},
AudioClipsUi, AudioClipsUi,
)); ));
}); });
@ -149,7 +135,7 @@ fn initialize_ui(mut commands: Commands) {
fn load_bogus( fn load_bogus(
mut events: EventReader<KeyboardInput>, mut events: EventReader<KeyboardInput>,
root: Query<Entity, (With<AnimationsUi>, Without<UiRef<Handle<AnimationClip>>>)>, root: Query<Entity, With<AnimationsUi>>,
mut commands: Commands, mut commands: Commands,
) { ) {
events events
@ -211,7 +197,7 @@ fn get_fname(asset_path: AssetPath, suffixes: &[&str]) -> String {
/// This should be a separate async system /// This should be a separate async system
fn manage_gltf_ui( fn manage_gltf_ui(
mut events: EventReader<AssetEvent<Gltf>>, mut events: EventReader<AssetEvent<Gltf>>,
root: Query<Entity, (With<GltfsUi>, Without<UiRef<Handle<Gltf>>>)>, root: Query<Entity, With<GltfsUi>>,
mut commands: Commands, mut commands: Commands,
server: Res<AssetServer>, server: Res<AssetServer>,
) { ) {
@ -228,9 +214,6 @@ fn manage_gltf_ui(
_ => None, _ => None,
}) })
.for_each(|(handle, name)| { .for_each(|(handle, name)| {
root.iter().for_each(|entity| {
commands.entity(entity).log_components();
});
commands commands
.spawn(( .spawn((
GameUiButton, GameUiButton,
@ -253,7 +236,7 @@ struct ScenesUi;
/// Sync scene assets with UI /// Sync scene assets with UI
fn manage_scene_ui( fn manage_scene_ui(
mut events: EventReader<AssetEvent<Scene>>, mut events: EventReader<AssetEvent<Scene>>,
root: Query<Entity, (With<ScenesUi>, Without<UiRef<Handle<Scene>>>)>, root: Query<Entity, With<ScenesUi>>,
mut commands: Commands, mut commands: Commands,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
registry: Res<AssetRegistry>, registry: Res<AssetRegistry>,
@ -269,6 +252,10 @@ fn manage_scene_ui(
|gltf_handle| match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) { |gltf_handle| match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) {
Some(gltf) => { Some(gltf) => {
gltf.named_scenes.iter().find_map(|(name, scene_handle)| { gltf.named_scenes.iter().find_map(|(name, scene_handle)| {
info!(
"scene_handle({:?}) == handle({:?})",
scene_handle, handle
);
(scene_handle == handle).then_some(name) (scene_handle == handle).then_some(name)
}) })
} }
@ -305,7 +292,7 @@ struct AnimationsUi;
fn manage_animation_ui( fn manage_animation_ui(
mut events: EventReader<AssetEvent<AnimationClip>>, mut events: EventReader<AssetEvent<AnimationClip>>,
root: Query<Entity, (With<AnimationsUi>, Without<UiRef<Handle<AnimationClip>>>)>, root: Query<Entity, With<AnimationsUi>>,
mut commands: Commands, mut commands: Commands,
gltfs: Res<Assets<Gltf>>, gltfs: Res<Assets<Gltf>>,
registry: Res<AssetRegistry>, registry: Res<AssetRegistry>,
@ -322,6 +309,10 @@ fn manage_animation_ui(
match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) { match gltfs.get(&gltf_handle.clone().typed::<Gltf>()) {
Some(gltf) => gltf.named_animations.iter().find_map( Some(gltf) => gltf.named_animations.iter().find_map(
|(name, animation_handle)| { |(name, animation_handle)| {
info!(
"animation_handle({:?}) == handle({:?})",
animation_handle, handle
);
(animation_handle == handle).then_some(name) (animation_handle == handle).then_some(name)
}, },
), ),
@ -351,83 +342,7 @@ fn manage_animation_ui(
struct AudioClipsUi; struct AudioClipsUi;
/// Drag+Drop import Audio to editor /// Drag+Drop import Audio to editor
fn load_audio( fn load_audio() {}
mut events: EventReader<FileDragAndDrop>,
server: Res<AssetServer>,
mut assets: ResMut<AssetRegistry>,
) {
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<AssetEvent<AudioSource>>,
root: Query<Entity, (With<AudioClipsUi>, Without<AudioSink>)>,
mut commands: Commands,
server: Res<AssetServer>,
) {
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<Interaction>, With<AudioClipsUi>),
>,
) {
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 /// Remove audio from editor
fn unload_audio() {} fn unload_audio() {}
@ -435,6 +350,9 @@ fn unload_audio() {}
/// Spawn Scene /// Spawn Scene
fn spawn_scene() {} fn spawn_scene() {}
/// Play/Loop Audio
fn play_audio() {}
/// Export level /// Export level
fn export_level() {} fn export_level() {}

@ -1,350 +1,101 @@
use bevy::{ use bevy::{input::mouse::MouseWheel, prelude::*};
input::{keyboard::KeyboardInput, ButtonState},
prelude::*,
window::PrimaryWindow,
};
use monologue_trees::{debug::*, ui::*};
fn main() { fn main() {
App::new() App::new()
.add_plugins(( .add_plugins(DefaultPlugins.set(WindowPlugin {
DefaultPlugins.set(WindowPlugin { primary_window: Some(Window {
primary_window: Some(Window { title: "UI WTF".into(),
title: "UI WTF".into(), resolution: (640., 480.).into(),
resolution: (640., 480.).into(),
..default()
}),
..default() ..default()
}), }),
GameUiPlugin,
))
// .init_resource::<Icon>()
.add_systems(Startup, init_ui2)
.add_systems(Update, toggle)
// .add_systems(Startup, init_ui)
// .add_systems(Update, (cursors, container()))
.run();
}
fn container() -> NodeBundle {
NodeBundle {
style: Style {
border: UiRect::all(Val::Px(2.0)),
right: Val::Percent(-100.0),
top: Val::Px(-4.0),
flex_direction: FlexDirection::Column,
display: Display::None,
..default()
},
background_color: BackgroundColor(Color::PURPLE),
border_color: BorderColor(Color::BLACK),
..default()
}
}
fn spec(color: Color) -> ButtonBundle {
ButtonBundle {
style: Style {
border: UiRect::all(Val::Px(2.0)),
width: Val::Px(100.0),
height: Val::Px(50.0),
flex_direction: FlexDirection::Column,
..default() ..default()
}, }))
background_color: BackgroundColor(color), .add_systems(Startup, (init,))
border_color: BorderColor(Color::BLACK), .add_systems(Update, (scroll,))
..default() .run();
}
}
fn toggle(
mut events: Query<
(&mut BackgroundColor, &Interaction, &Children),
(Changed<Interaction>, With<Button>),
>,
mut styles: Query<&mut Style>,
) {
events
.iter_mut()
.for_each(|(mut bg_color, interaction, children)| match interaction {
Interaction::Pressed => {
bg_color.0 = Color::RED;
children.iter().for_each(|&child| {
if let Ok(mut style) = styles.get_mut(child) {
style.display = match style.display {
Display::Flex => Display::None,
Display::None | _ => Display::Flex,
};
}
});
}
Interaction::Hovered => {
bg_color.0 = Color::RED;
}
Interaction::None => {
bg_color.0 = Color::PINK;
}
});
} }
// const CURSORS: [CursorIcon; 35] = [ #[derive(Component)]
// CursorIcon::Default, struct ScrollingList;
// CursorIcon::Crosshair,
// CursorIcon::Hand,
// CursorIcon::Arrow,
// CursorIcon::Move,
// CursorIcon::Text,
// CursorIcon::Wait,
// CursorIcon::Help,
// CursorIcon::Progress,
// CursorIcon::NotAllowed,
// CursorIcon::ContextMenu,
// CursorIcon::Cell,
// CursorIcon::VerticalText,
// CursorIcon::Alias,
// CursorIcon::Copy,
// CursorIcon::NoDrop,
// CursorIcon::Grab,
// CursorIcon::Grabbing,
// CursorIcon::AllScroll,
// CursorIcon::ZoomIn,
// CursorIcon::ZoomOut,
// CursorIcon::EResize,
// CursorIcon::NResize,
// CursorIcon::NeResize,
// CursorIcon::NwResize,
// CursorIcon::SResize,
// CursorIcon::SeResize,
// CursorIcon::SwResize,
// CursorIcon::WResize,
// CursorIcon::EwResize,
// CursorIcon::NsResize,
// CursorIcon::NeswResize,
// CursorIcon::NwseResize,
// CursorIcon::ColResize,
// CursorIcon::RowResize,
// ];
fn init_ui2(mut commands: Commands) { fn init(mut commands: Commands) {
commands.spawn(( info!("Spawning camera");
Camera2dBundle { ..default() }, commands.spawn(Camera2dBundle { ..default() });
UiCameraConfig { show_ui: true },
));
info!("Initializing UI");
commands commands
.spawn(NodeBundle { .spawn(NodeBundle {
background_color: BackgroundColor(Color::WHITE),
style: Style { style: Style {
border: UiRect::all(Val::Px(2.0)), justify_content: JustifyContent::Center,
flex_direction: FlexDirection::Column, width: Val::Percent(90.0),
height: Val::Percent(90.0),
overflow: Overflow::clip(),
..default() ..default()
}, },
background_color: BackgroundColor(Color::PURPLE),
border_color: BorderColor(Color::BLACK),
..default() ..default()
}) })
.with_children(|parent| { .with_children(|parent| {
parent.spawn(spec(Color::PINK)).with_children(|parent| { parent
parent.spawn(container()).with_children(|parent| { .spawn((
parent.spawn(spec(Color::PINK)).with_children(|parent| { NodeBundle {
parent.spawn(container()).with_children(|parent| { background_color: BackgroundColor(Color::OLIVE),
parent.spawn(spec(Color::PINK)); style: Style {
parent.spawn(spec(Color::PINK)); flex_wrap: FlexWrap::Wrap,
parent.spawn(spec(Color::PINK)); flex_direction: FlexDirection::Row,
justify_content: JustifyContent::SpaceAround,
width: Val::Auto,
height: Val::Auto,
max_width: Val::Auto,
max_height: Val::Auto,
..default()
},
..default()
},
ScrollingList,
))
.with_children(|parent| {
info!("Initializing Child Element");
let colors = [
Color::ORANGE,
Color::BLUE,
Color::GREEN,
Color::SALMON,
Color::SEA_GREEN,
Color::MAROON,
Color::ORANGE,
Color::BLUE,
Color::GREEN,
Color::SALMON,
Color::SEA_GREEN,
Color::MAROON,
];
for i in 0..12 {
parent.spawn(NodeBundle {
background_color: BackgroundColor(colors[i]),
style: Style {
width: Val::Px(256.0),
height: Val::Px(256.0),
padding: UiRect::all(Val::Px(5.0)),
..default()
},
..default()
}); });
}); }
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
parent.spawn(spec(Color::PINK)).with_children(|parent| {
parent.spawn(container()).with_children(|parent| {
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
parent.spawn(spec(Color::PINK));
});
});
}); });
});
}); });
} }
// #[derive(Debug, Component, Resource, Default)] fn scroll(
// struct Icon(CursorIcon); mut scroll_evr: EventReader<MouseWheel>,
// mut query: Query<&mut Style, With<ScrollingList>>,
// fn init_ui(mut commands: Commands) { ) {
// commands.spawn(( for ev in scroll_evr.iter() {
// Camera2dBundle { ..default() }, for mut s in query.iter_mut() {
// UiCameraConfig { show_ui: true }, s.top = match s.top {
// )); Val::Px(current) => Val::Px(current + (ev.y * 5.0)),
// _ => Val::Px(0.0),
// commands };
// .spawn(NodeBundle { }
// style: Style { }
// height: Val::Percent(100.0), }
// width: Val::Percent(100.0),
// justify_content: JustifyContent::Start,
// align_items: AlignItems::Start,
// ..default()
// },
// background_color: BackgroundColor(Color::GRAY),
// ..default()
// })
// .with_children(|parent| {
// parent
// .spawn((
// GameUiNav,
// // Name::new("Game Nav"),
// NodeBundle { ..default() },
// ))
// .with_children(|parent| {
// parent
// .spawn((
// GameUiTab,
// Name::new("Grow/Shrink Tab"),
// NodeBundle { ..default() },
// ))
// .with_children(|parent| {
// parent.spawn((
// container,
// GameUiSet,
// // Name::new("Grow/Shrink Set"),
// NodeBundle { ..default() },
// ));
// });
//
// parent
// .spawn((
// GameUiTab,
// Name::new("Cursor Icons Tab"),
// NodeBundle { ..default() },
// ))
// .with_children(|parent| {
// parent
// .spawn((
// GameUiSet,
// // Name::new("Cursor Icons Set"),
// NodeBundle { ..default() },
// ))
// .with_children(|parent| {
// CURSORS.iter().for_each(|&icon| {
// parent.spawn((
// GameUiButton,
// Name::new(format!("{:?}", icon)),
// NodeBundle { ..default() },
// Icon(icon),
// ));
// });
// });
// });
// });
// });
// }
//
// fn cursors(
// events: Query<(&Interaction, &Icon), (Changed<Interaction>, With<Button>)>,
// mut primary_window: Query<&mut Window, With<PrimaryWindow>>,
// mut curr: ResMut<Icon>,
// ) {
// events.iter().for_each(|(&interaction, &ref icon)| {
// let mut window = primary_window.single_mut();
//
// match interaction {
// Interaction::Hovered => {
// (*window).cursor.icon = icon.0.clone();
// }
// Interaction::Pressed => {
// curr.0 = icon.0.clone();
// }
// Interaction::None => {
// (*window).cursor.icon = curr.0.clone();
// }
// }
// })
// }
//
// #[derive(Debug, Component)]
// struct container;
//
// fn container(
// mut events: EventReader<KeyboardInput>,
// mut commands: Commands,
// root: Query<Entity, With<container>>,
// children: Query<&Children, With<container>>,
// ) {
// events.iter().for_each(
// |KeyboardInput {
// key_code, state, ..
// }| {
// match (key_code, state) {
// (Some(KeyCode::Up), ButtonState::Pressed) => {
// commands.entity(root.single()).with_children(|parent| {
// parent.spawn((
// GameUiButton,
// Name::new("asdfwtf"),
// NodeBundle { ..default() },
// ));
// });
// }
// (Some(KeyCode::Down), ButtonState::Pressed) => {
// children.single().iter().last().iter().for_each(|&&child| {
// commands.entity(child).despawn_recursive();
// });
// }
// _ => (),
// }
// },
// )
// }

@ -1,101 +1,121 @@
/// TODO:
/// * Titles on top left of window/tab
/// *
use bevy::{prelude::*, window::PrimaryWindow}; use bevy::{prelude::*, window::PrimaryWindow};
pub struct GameUiPlugin; pub struct GameUiPlugin;
impl Plugin for GameUiPlugin { impl Plugin for GameUiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(PreUpdate, (spawn_nav, spawn_tab, spawn_set, spawn_button)) app.add_systems(
.add_systems(Update, (manage_names, manage_tab)); Update,
(
manage_ui_list,
manage_ui_set,
manage_ui_button,
manage_cursor,
),
);
} }
} }
/// Navigation UI Container /// GameUiList for holding ordered collections of objects
/// Buttons in a nav correspond to Tabs in a navbar/navmenu #[derive(Debug, Component)]
#[derive(Component, PartialEq)] pub struct GameUiList;
pub struct GameUiNav;
/// Sets contain a variety of elements like action buttons /// Manage UI Lists: lists of UI entities.
/// Usually the "leaf" of a navigation tree fn manage_ui_list(events: Query<(Entity, &Name), Added<GameUiList>>, mut commands: Commands) {
#[derive(Component, PartialEq)] events.iter().for_each(|(entity, name)| {
pub struct GameUiSet; commands
.entity(entity)
/// Buttons are used for interaction in the UI .insert(NodeBundle {
#[derive(Component, PartialEq)] style: Style {
pub struct GameUiButton; // width: Val::Px(100.0),
margin: UiRect::all(Val::Px(2.0)),
#[derive(Component, PartialEq)] padding: UiRect::all(Val::Px(2.0)),
pub struct GameUiTab; border: UiRect::all(Val::Px(2.0)),
flex_direction: FlexDirection::Column,
fn spawn_nav(events: Query<(Entity, &GameUiNav), Added<GameUiNav>>, mut commands: Commands) { align_items: AlignItems::Stretch,
events.iter().for_each(|(entity, _)| { justify_items: JustifyItems::Center,
info!("Spawning Nav UI"); align_content: AlignContent::FlexStart,
..default()
},
background_color: BackgroundColor(Color::RED),
border_color: BorderColor(Color::BLACK),
..default()
})
.with_children(|parent| {
parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
});
}); });
} }
fn spawn_tab(events: Query<Entity, Added<GameUiTab>>, mut commands: Commands) { /// GameUiSet Component for holding collections of objects
events.iter().for_each(|entity| {}); #[derive(Debug, Component)]
} pub struct GameUiSet;
fn spawn_set(events: Query<Entity, Added<GameUiSet>>, mut commands: Commands) {
events.iter().for_each(|entity| {});
}
fn spawn_button( /// Manage UI Sets: collections of UI entities.
events: Query<(Entity, &GameUiButton), Added<GameUiButton>>, fn manage_ui_set(events: Query<(Entity, &Name), Added<GameUiSet>>, mut commands: Commands) {
mut commands: Commands, events.iter().for_each(|(entity, name)| {
) { commands
events.iter().for_each(|(entity, _)| { .entity(entity)
info!("Spawning UI Button"); .insert(NodeBundle {
commands.entity(entity).insert(ButtonBundle { style: Style {
style: Style { // width: Val::Px(100.0),
padding: UiRect::all(Val::Px(3.0)), margin: UiRect::all(Val::Px(2.0)),
margin: UiRect::all(Val::Px(3.0)), padding: UiRect::all(Val::Px(2.0)),
border: UiRect::all(Val::Px(2.0)),
align_items: AlignItems::FlexStart,
align_content: AlignContent::FlexStart,
flex_direction: FlexDirection::Row,
flex_wrap: FlexWrap::Wrap,
..default()
},
background_color: BackgroundColor(Color::BLUE),
border_color: BorderColor(Color::BLACK),
..default() ..default()
}, })
background_color: BackgroundColor(Color::BLUE), .with_children(|parent| {
..default() parent.spawn(TextBundle::from_section(name, TextStyle { ..default() }));
}); });
}); });
} }
fn manage_names( /// GameUiButton for interactive elements
events: Query< #[derive(Debug, Component)]
(Entity, &Name), pub struct GameUiButton;
(
Or<(With<GameUiNav>, With<GameUiButton>)>, /// Manage UI Buttons. interactive buttons.
Or<(Added<Name>, Changed<Name>)>, fn manage_ui_button(events: Query<(Entity, &Name), Added<GameUiButton>>, mut commands: Commands) {
),
>,
mut commands: Commands,
) {
events.iter().for_each(|(entity, name)| { events.iter().for_each(|(entity, name)| {
commands.entity(entity).with_children(|parent| { commands
parent .entity(entity)
.spawn(NodeBundle { .insert(ButtonBundle {
style: Style { style: Style {
align_self: AlignSelf::FlexStart, margin: UiRect::all(Val::Px(2.0)),
padding: UiRect::all(Val::Px(3.0)), padding: UiRect::all(Val::Px(2.0)),
margin: UiRect::all(Val::Px(3.0)), border: UiRect::all(Val::Px(2.0)),
..default() justify_content: JustifyContent::Center,
},
background_color: BackgroundColor(Color::CRIMSON),
..default() ..default()
}) },
.with_children(|parent| { background_color: BackgroundColor(Color::GREEN),
parent.spawn(TextBundle::from_section( border_color: BorderColor(Color::BLACK),
name.as_str(), ..default()
TextStyle { ..default() }, })
)); .with_children(|parent| {
}); parent.spawn(TextBundle::from_section(name, TextStyle::default()));
}); });
}); });
} }
fn manage_tab(events: Query<&Interaction, Changed<Interaction>>) { /// Manage the cursor icon for better immersion
events.iter().for_each(|interaction| { fn manage_cursor(
info!("When tab is clicked, show/hide content"); mut primary_window: Query<&mut Window, With<PrimaryWindow>>,
events: Query<&Interaction, With<Interaction>>,
) {
events.iter().for_each(|event| {
let mut window = primary_window.single_mut();
window.cursor.icon = match event {
Interaction::Pressed => CursorIcon::Grabbing,
Interaction::Hovered => CursorIcon::Hand,
Interaction::None => CursorIcon::Default,
}
}); });
} }

14
tmp

@ -0,0 +1,14 @@
if *duration > Duration::ZERO {
let mut text = texts.single_mut();
let total_sections = text.sections.len();
*duration = duration.saturating_sub(time.delta());
for (idx, section) in text.sections.iter_mut().enumerate() {
let ratio = ((idx + 1) as f32) / (total_sections as f32);
let cursor = 1.0 - ((*duration).as_secs_f32() / 30.0);
let alpha = if cursor > ratio { 1.0 } else { 0.0 };
section.style.color.set_a(alpha);
}
}
Loading…
Cancel
Save