/*
* This file is part of laurelin_client
* Copyright (C) 2023 Jonni Liljamo <jonni@liljamo.com>
*
* Licensed under GPL-3.0-only.
* See LICENSE for licensing information.
*/
use bevy::{prelude::*, input::mouse::{MouseScrollUnit, MouseWheel}, a11y::{AccessibilityNode, accesskit::NodeBuilder}};
use crate::AppState;
pub struct LogPlugin;
impl Plugin for LogPlugin {
fn build(&self, app: &mut App) {
app.add_event::<ToggleLogEvent>()
.add_event::<UpdateLogEvent>()
.add_system(setup_log.in_schedule(OnEnter(AppState::InGame)))
.add_system(update_log_button)
.add_system(toggle_log.run_if(on_event::<ToggleLogEvent>()))
.add_system(update_log.run_if(on_event::<UpdateLogEvent>()))
.add_system(log_mouse_scroll);
}
}
pub struct ToggleLogEvent;
pub struct UpdateLogEvent;
const NORMAL_BUTTON: Color = Color::rgb(0.15, 0.15, 0.15);
const HOVERED_BUTTON: Color = Color::rgb(0.25, 0.25, 0.25);
const PRESSED_BUTTON: Color = Color::rgb(0.35, 0.75, 0.35);
#[derive(Component)]
struct LogButton;
#[derive(Component)]
struct LogList;
fn setup_log(mut commands: Commands, asset_server: Res<AssetServer>) {
// setup a button for log toggle
// setup the log ui itself, with hidden visibility
let font = asset_server.load("fonts/FiraMono-Bold.ttf");
// log toggle button
commands
.spawn(NodeBundle {
style: Style {
position_type: PositionType::Absolute,
position: UiRect {
top: Val::Px(20.),
right: Val::Px(20.),
..Default::default()
},
..Default::default()
},
..Default::default()
})
.with_children(|parent| {
parent
.spawn((
ButtonBundle {
style: Style {
size: Size::new(Val::Px(75.), Val::Px(35.)),
// center child text
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..Default::default()
},
background_color: NORMAL_BUTTON.into(),
..Default::default()
},
LogButton,
))
.with_children(|parent| {
parent.spawn((
TextBundle::from_section(
"Log",
TextStyle {
font: font.clone(),
font_size: 20.,
color: Color::WHITE,
},
),
));
});
});
// log window
commands
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
size: Size {
width: Val::Px(300.),
height: Val::Px(300.),
},
position_type: PositionType::Absolute,
position: UiRect {
bottom: Val::Percent(12.5),
right: Val::Px(100.),
..Default::default()
},
..Default::default()
},
background_color: Color::rgb(0.15, 0.15, 0.15).into(),
..Default::default()
})
.with_children(|parent| {
parent
.spawn(NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
align_self: AlignSelf::Stretch,
size: Size::all(Val::Percent(95.)),
overflow: Overflow::Hidden,
..Default::default()
},
..Default::default()
})
.with_children(|parent| {
parent
.spawn((
NodeBundle {
style: Style {
flex_direction: FlexDirection::Column,
max_size: Size::UNDEFINED,
..Default::default()
},
..Default::default()
},
AccessibilityNode(NodeBuilder::new(bevy::a11y::accesskit::Role::List)),
ScrollingList::default(),
LogList,
))
.with_children(|parent| {
for i in 0..30 {
parent.spawn((
TextBundle::from_section(
format!("Item {i}"),
TextStyle {
font: font.clone(),
font_size: 20.,
color: Color::WHITE,
},
),
AccessibilityNode(NodeBuilder::new(bevy::a11y::accesskit::Role::ListItem)),
));
}
});
});
});
}
fn update_log_button(
mut interaction_query: Query<
(&Interaction, &mut BackgroundColor),
(Changed<Interaction>, With<LogButton>),
>,
mut ul_ev_w: EventWriter<UpdateLogEvent>,
) {
for (interaction, mut color) in &mut interaction_query {
match interaction {
Interaction::Clicked => {
*color = PRESSED_BUTTON.into();
ul_ev_w.send(UpdateLogEvent);
}
Interaction::Hovered => {
*color = HOVERED_BUTTON.into();
}
Interaction::None => {
*color = NORMAL_BUTTON.into();
}
}
}
}
fn toggle_log() {
// toggle the node visibility, see the state button for e.g
}
fn update_log() {
// despawn the children ui nodes, and create new ones.
// update log ui scrolling items with... log items
}
#[derive(Component, Default)]
struct ScrollingList {
position: f32,
}
fn log_mouse_scroll(
mut mouse_wheel_events: EventReader<MouseWheel>,
mut query_list: Query<(&mut ScrollingList, &mut Style, &Parent, &Node)>,
query_node: Query<&Node>,
) {
for mouse_wheel_event in mouse_wheel_events.iter() {
for (mut scrolling_list, mut style, parent, list_node) in &mut query_list {
let items_height = list_node.size().y;
let container_height = query_node.get(parent.get()).unwrap().size().y;
let max_scroll = (items_height - container_height).max(0.);
let dy = match mouse_wheel_event.unit {
MouseScrollUnit::Line => mouse_wheel_event.y * 20.,
MouseScrollUnit::Pixel => mouse_wheel_event.y,
};
scrolling_list.position += dy;
scrolling_list.position = scrolling_list.position.clamp(-max_scroll, 0.);
style.position.top = Val::Px(scrolling_list.position);
}
}
}