use std::collections::HashMap;
use bevy_ecs::prelude::*;
use super::geometry::{ButtonIcons, Icon, Position, Size};
use super::icons::{ours, path};
use super::layout::{self, Expand};
use super::menu::Submenu;
use super::panel::PanelChild;
use super::rect::Rect;
use super::resources::{CursorPosition, ScreenSize};
use super::visibility::Visibility;
struct Row {
entity: Entity,
panel: Entity,
rect: Rect,
opens: Rect,
rows: Vec<Rect>,
side: Expand,
panel_entity: Entity,
buttons: Vec<Entity>,
pointer: Entity,
showing: bool,
}
pub fn update_submenu_system(
screen: Res<ScreenSize>,
cursor: Res<CursorPosition>,
parents: Query<(Entity, &Position, &Size, &Submenu, Option<&PanelChild>)>,
mut rects: Query<(&mut Position, &mut Size), Without<Submenu>>,
mut visibilities: Query<&mut Visibility>,
mut icons: Query<&mut Icon>,
mut carets: Query<&mut ButtonIcons>,
) {
let mut rows = Vec::new();
for (entity, row_pos, row_size, submenu, child_of) in &parents {
let rect = row_pos.rect(row_size);
let panel = child_of.map(|child| child.0).unwrap_or(entity);
let parent_panel = rects
.get(panel)
.map(|(pos, size)| pos.rect(size))
.unwrap_or(rect);
let (opens, opened_rows) = layout::submenu_rect(
parent_panel,
rect,
submenu.buttons.len(),
submenu.metrics,
submenu.expand,
submenu.preferred,
screen.width,
screen.height,
);
let side = match opens.center_x() < parent_panel.center_x() {
true => Expand::Left,
false => Expand::Right,
};
let showing = visibilities
.get(submenu.panel)
.map(|visibility| visibility.0)
.unwrap_or(false);
rows.push(Row {
entity,
panel,
rect,
opens,
rows: opened_rows,
side,
panel_entity: submenu.panel,
buttons: submenu.buttons.clone(),
pointer: submenu.pointer,
showing,
});
}
let mut open: HashMap<Entity, Entity> = HashMap::new();
for row in &rows {
if row.rect.contains(cursor.x, cursor.y) {
open.insert(row.panel, row.entity);
}
}
for row in &rows {
if row.showing && row.opens.contains(cursor.x, cursor.y) {
open.entry(row.panel).or_insert(row.entity);
}
}
for row in &rows {
let (pointer_rect, pointer_icon) = pointer_for(row.opens, row.rect, row.side);
let mut place = |entity: Entity, rect: Rect| {
if let Ok((mut pos, mut size)) = rects.get_mut(entity) {
(pos.x, pos.y) = (rect.x, rect.y);
(size.width, size.height) = (rect.width, rect.height);
}
};
place(row.panel_entity, row.opens);
for (&button, &rect) in row.buttons.iter().zip(&row.rows) {
place(button, rect);
}
place(row.pointer, pointer_rect);
if let Ok(mut icon) = icons.get_mut(row.pointer) {
icon.path = pointer_icon;
}
if let Ok(mut wears) = carets.get_mut(row.entity) {
wears.trailing = Some(caret_for(row.side));
}
let showing = open.get(&row.panel) == Some(&row.entity);
for entity in [row.panel_entity, row.pointer]
.into_iter()
.chain(row.buttons.iter().copied())
{
if let Ok(mut visibility) = visibilities.get_mut(entity) {
*visibility = Visibility(showing);
}
}
}
}
fn pointer_for(panel: Rect, row: Rect, opened: Expand) -> (Rect, &'static str) {
let size = layout::POINTER_SIZE;
let y = (row.y + row.height * 0.5 - size * 0.5)
.clamp(panel.y, (panel.y + panel.height - size).max(panel.y));
match opened {
Expand::Left => (
Rect::new(panel.x + panel.width, y, size, size),
ours::POINTER_RIGHT,
),
_ => (Rect::new(panel.x - size, y, size, size), ours::POINTER_LEFT),
}
}
pub fn caret_for(opened: Expand) -> &'static str {
match opened {
Expand::Left => path::CARET_LEFT,
_ => path::CARET_RIGHT,
}
}