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

@ -1,350 +1,101 @@
use bevy::{
input::{keyboard::KeyboardInput, ButtonState},
prelude::*,
window::PrimaryWindow,
};
use monologue_trees::{debug::*, ui::*};
use bevy::{input::mouse::MouseWheel, prelude::*};
fn main() {
App::new()
.add_plugins((
DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "UI WTF".into(),
resolution: (640., 480.).into(),
..default()
}),
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "UI WTF".into(),
resolution: (640., 480.).into(),
..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()
},
background_color: BackgroundColor(color),
border_color: BorderColor(Color::BLACK),
..default()
}
}
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;
}
});
}))
.add_systems(Startup, (init,))
.add_systems(Update, (scroll,))
.run();
}
// const CURSORS: [CursorIcon; 35] = [
// CursorIcon::Default,
// 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,
// ];
#[derive(Component)]
struct ScrollingList;
fn init_ui2(mut commands: Commands) {
commands.spawn((
Camera2dBundle { ..default() },
UiCameraConfig { show_ui: true },
));
fn init(mut commands: Commands) {
info!("Spawning camera");
commands.spawn(Camera2dBundle { ..default() });
info!("Initializing UI");
commands
.spawn(NodeBundle {
background_color: BackgroundColor(Color::WHITE),
style: Style {
border: UiRect::all(Val::Px(2.0)),
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
width: Val::Percent(90.0),
height: Val::Percent(90.0),
overflow: Overflow::clip(),
..default()
},
background_color: BackgroundColor(Color::PURPLE),
border_color: BorderColor(Color::BLACK),
..default()
})
.with_children(|parent| {
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((
NodeBundle {
background_color: BackgroundColor(Color::OLIVE),
style: Style {
flex_wrap: FlexWrap::Wrap,
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)]
// struct Icon(CursorIcon);
//
// fn init_ui(mut commands: Commands) {
// commands.spawn((
// Camera2dBundle { ..default() },
// UiCameraConfig { show_ui: true },
// ));
//
// 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();
// });
// }
// _ => (),
// }
// },
// )
// }
fn scroll(
mut scroll_evr: EventReader<MouseWheel>,
mut query: Query<&mut Style, With<ScrollingList>>,
) {
for ev in scroll_evr.iter() {
for mut s in query.iter_mut() {
s.top = match s.top {
Val::Px(current) => Val::Px(current + (ev.y * 5.0)),
_ => Val::Px(0.0),
};
}
}
}

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