mod attention;
mod better;
mod charts;
mod milestones;
mod rows;
mod todo;
mod verdict;
#[cfg(test)]
mod better_tests;
#[cfg(test)]
mod tests;
use bevy_app::{App, Startup, Update};
use bevy_ecs::change_detection::DetectChangesMut;
use bevy_ecs::hierarchy::ChildOf;
use bevy_ecs::prelude::{Commands, Entity, IntoScheduleConfigs, Query, Res, ResMut, With};
use bevy_ui::{FlexDirection, Node, Val};
pub(crate) use better::Better;
#[cfg(test)]
pub(crate) use charts::OverviewChart;
pub(crate) use charts::cycle_chart;
use rows::List;
pub(crate) use rows::edit_row;
pub(crate) use todo::WARNING_MARK;
use super::command::Outcome;
use super::hints::Hints;
use super::layout::{self, Body};
use super::nav::{self, Page};
use super::pane::Pane;
use super::session::{Projected, Today, YearCursor, span};
pub fn plugin(app: &mut App) {
app.init_resource::<charts::ChartView>();
app.init_resource::<Better>();
app.add_systems(Startup, spawn_overview.after(layout::spawn_frame));
app.add_systems(
Update,
(
verdict::refresh,
todo::refresh,
(better::work, rows::refresh).chain(),
charts::refresh,
),
);
}
const YEAR_HINTS: Hints = Hints(&[("←→", "year")]);
const SCROLL_HINTS: Hints = Hints(&[("↑↓", "scroll")]);
const PENDING: &str = "…";
const LISTS_BAND: (f32, f32) = (6.0, 1.0);
const CHART_BAND: (f32, f32) = (10.0, 3.0);
const BETTER_BAND: (f32, f32) = (4.0, 1.0);
const CHART_SHARE: f32 = 3.0;
const TODO_SHARE: f32 = 2.0;
fn spawn_overview(bodies: Query<Entity, With<Body>>, mut commands: Commands) {
let Ok(body) = bodies.single() else {
return;
};
let view = nav::spawn_surface(&mut commands, body, Some(Page::Overview));
commands.entity(view).insert(YEAR_HINTS);
verdict::spawn(&mut commands, view);
let lists = spawn_band(&mut commands, view, LISTS_BAND);
rows::spawn(&mut commands, lists, &[List::Milestones, List::Attention]);
let charted = spawn_band(&mut commands, view, CHART_BAND);
charts::spawn(&mut commands, charted, CHART_SHARE);
todo::spawn(&mut commands, charted, TODO_SHARE);
let better = spawn_band(&mut commands, view, BETTER_BAND);
rows::spawn(&mut commands, better, &[List::Better]);
}
fn spawn_band(commands: &mut Commands, view: Entity, (least, share): (f32, f32)) -> Entity {
let band = Node {
flex_direction: FlexDirection::Row,
flex_grow: share,
flex_basis: Val::Px(0.0),
min_height: Val::Px(least),
..Node::default()
};
commands.spawn((band, ChildOf(view))).id()
}
fn spawn_list(commands: &mut Commands, band: Entity, title: &str, share: f32) -> Entity {
let pane = Pane::new(title).sharing(share).spawn(commands, band);
layout::spawn_scrolled_list(commands, pane, SCROLL_HINTS)
}
fn step_year(
projected: &Projected,
today: Today,
cursor: &mut ResMut<YearCursor>,
step: i16,
) -> Outcome {
let planned = span(&projected.projection.years);
let year = cursor.resolve(today, planned, planned);
let stepped = (year + step).clamp(planned.0, planned.1);
if stepped != year {
cursor.set_if_neq(YearCursor(Some(stepped)));
}
Outcome::Done
}
pub(crate) fn next_year(
projected: Res<Projected>,
today: Res<Today>,
mut cursor: ResMut<YearCursor>,
) -> Outcome {
step_year(&projected, *today, &mut cursor, 1)
}
pub(crate) fn previous_year(
projected: Res<Projected>,
today: Res<Today>,
mut cursor: ResMut<YearCursor>,
) -> Outcome {
step_year(&projected, *today, &mut cursor, -1)
}