/* * This file is part of laurelin_client * Copyright (C) 2023 Jonni Liljamo * * 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::() .add_event::() .add_system(setup_log.in_schedule(OnEnter(AppState::InGame))) .add_system(update_log_button) .add_system(toggle_log.run_if(on_event::())) .add_system(update_log.run_if(on_event::())) .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) { // 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, With), >, mut ul_ev_w: EventWriter, ) { 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, 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); } } }