use crate::types::{GraphNode, NodeHighlight, SearchBox, SearchState};
use bevy::prelude::*;
pub fn setup_search_ui(commands: &mut Commands) {
commands.spawn((
Text::new(""),
TextFont {
font_size: 24.0,
..default()
},
TextColor(Color::WHITE),
Node {
position_type: PositionType::Absolute,
bottom: Val::Px(50.0),
left: Val::Percent(50.0),
padding: UiRect::all(Val::Px(10.0)),
..default()
},
BackgroundColor(Color::srgba(0.0, 0.0, 0.0, 0.8)),
Visibility::Hidden,
SearchBox,
));
}
pub fn toggle_search(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut search_state: ResMut<SearchState>,
mut search_box_query: Query<&mut Visibility, With<SearchBox>>,
) {
if keyboard_input.just_pressed(KeyCode::Slash) && !search_state.active {
search_state.active = true;
search_state.query.clear();
search_state.matching_nodes.clear();
if let Ok(mut visibility) = search_box_query.single_mut() {
*visibility = Visibility::Visible;
}
} else if keyboard_input.just_pressed(KeyCode::Escape) && search_state.active {
search_state.active = false;
search_state.query.clear();
search_state.matching_nodes.clear();
if let Ok(mut visibility) = search_box_query.single_mut() {
*visibility = Visibility::Hidden;
}
}
}
pub fn handle_search_input(
keyboard_input: Res<ButtonInput<KeyCode>>,
mut search_state: ResMut<SearchState>,
mut search_box_query: Query<&mut Text, With<SearchBox>>,
node_query: Query<(Entity, &GraphNode, &GlobalTransform)>,
mut commands: Commands,
) {
if !search_state.active {
return;
}
for (key, ch) in [
(KeyCode::KeyA, 'a'),
(KeyCode::KeyB, 'b'),
(KeyCode::KeyC, 'c'),
(KeyCode::KeyD, 'd'),
(KeyCode::KeyE, 'e'),
(KeyCode::KeyF, 'f'),
(KeyCode::KeyG, 'g'),
(KeyCode::KeyH, 'h'),
(KeyCode::KeyI, 'i'),
(KeyCode::KeyJ, 'j'),
(KeyCode::KeyK, 'k'),
(KeyCode::KeyL, 'l'),
(KeyCode::KeyM, 'm'),
(KeyCode::KeyN, 'n'),
(KeyCode::KeyO, 'o'),
(KeyCode::KeyP, 'p'),
(KeyCode::KeyQ, 'q'),
(KeyCode::KeyR, 'r'),
(KeyCode::KeyS, 's'),
(KeyCode::KeyT, 't'),
(KeyCode::KeyU, 'u'),
(KeyCode::KeyV, 'v'),
(KeyCode::KeyW, 'w'),
(KeyCode::KeyX, 'x'),
(KeyCode::KeyY, 'y'),
(KeyCode::KeyZ, 'z'),
(KeyCode::Space, ' '),
] {
if keyboard_input.just_pressed(key) {
if keyboard_input.pressed(KeyCode::ShiftLeft)
|| keyboard_input.pressed(KeyCode::ShiftRight)
{
search_state.query.push(ch.to_ascii_uppercase());
} else {
search_state.query.push(ch);
}
break;
}
}
if keyboard_input.just_pressed(KeyCode::Backspace) {
search_state.query.pop();
}
if let Ok(mut text) = search_box_query.single_mut() {
text.0 = format!("Search: {}_", search_state.query);
}
search_state.matching_nodes.clear();
if !search_state.query.is_empty() {
for (entity, node, _) in &node_query {
if node
.name
.to_lowercase()
.contains(&search_state.query.to_lowercase())
{
search_state.matching_nodes.push(entity);
}
}
}
search_state.selected_node = search_state.matching_nodes.first().copied();
for (entity, _, _) in &node_query {
if search_state.matching_nodes.contains(&entity) {
commands
.entity(entity)
.try_insert(NodeHighlight { fade_timer: 1.0 });
} else {
commands.entity(entity).remove::<NodeHighlight>();
}
}
}
pub fn update_node_highlighting(
mut commands: Commands,
mut highlight_query: Query<(Entity, &mut NodeHighlight)>,
time: Res<Time>,
search_state: Res<SearchState>,
) {
let delta = time.delta_secs();
for (entity, mut highlight) in &mut highlight_query {
if search_state.active {
highlight.fade_timer = 1.0;
} else {
highlight.fade_timer -= delta * 0.05;
if highlight.fade_timer <= 0.0 {
commands.entity(entity).remove::<NodeHighlight>();
}
}
}
}
pub fn apply_highlight_visuals(
node_query: Query<(&MeshMaterial3d<StandardMaterial>, Option<&NodeHighlight>), With<GraphNode>>,
mut materials: ResMut<Assets<StandardMaterial>>,
) {
for (material, highlight) in &node_query {
let material_handle = &material.0;
if let Some(material) = materials.get_mut(material_handle) {
if let Some(highlight) = highlight {
let intensity = highlight.fade_timer;
material.emissive =
LinearRgba::new(intensity * 0.5, intensity * 0.5, intensity * 0.0, 1.0);
} else {
material.emissive = LinearRgba::BLACK;
}
}
}
}