rpg_base/character_window/src/content.rs

218 lines
5.9 KiB
Rust

use std::{
marker::PhantomData,
sync::{Arc, Weak},
};
use crate::*;
pub trait ContentWrapper: ContentUpdate + Send + Sync {
fn refresh(&mut self, world: &mut World) -> Result<()>;
fn next_tab(&mut self, world: &mut World, hero: Entity) -> Result<()>;
fn previous_tab(&mut self, world: &mut World, hero: Entity) -> Result<()>;
fn base(&self) -> &Arc<GuiSnippet>;
fn is_empty(&self) -> bool;
}
pub trait ContentUpdate {
fn update(&mut self, world: &mut World, hero: Entity) -> Result<()>;
fn select(&self, world: &mut World) -> Result<()>;
}
pub struct Content<A: Ability + 'static, T: Send + Sync> {
pub reference: Weak<CharacterWindow>,
base: Arc<GuiSnippet>,
data: Vec<T>,
on_enable: Box<dyn Fn(&mut World) -> Result<Vec<T>> + Send + Sync + 'static>,
page: usize,
pages: usize,
ability_marker: PhantomData<A>,
}
impl<A: Ability + 'static, T: Send + Sync> Content<A, T> {
pub fn new<F, P>(
world: &mut World,
reference: Weak<CharacterWindow>,
on_enable: F,
) -> Result<Self>
where
F: Fn(&mut World) -> Result<Vec<T>> + Send + Sync + 'static,
P: Page,
{
let base = GuiSnippet::from_str(world, include_str!("../resources/content.xml"))?;
let (gui_handler, engine_settings, context): (
&mut GuiHandler,
&mut EngineSettings,
&mut Context,
) = world.resources.get_mut()?;
let left: Arc<Button> = base.element("left")?;
left.set_text(gui_handler, "<")?;
left.set_info_icon(
gui_handler,
&engine_settings.controller_icon(context, ControllerButton::LeftTrigger)?,
)?;
left.set_callback({
let reference = reference.clone();
move |world| {
if let Some(window) = reference.upgrade() {
let mut tab = window.tab_mut();
let page = tab.downcast_mut::<P>();
page.previous_tab(world)?;
}
Ok(())
}
});
let right: Arc<Button> = base.element("right")?;
right.set_text(gui_handler, ">")?;
right.set_info_icon(
gui_handler,
&engine_settings.controller_icon(context, ControllerButton::RightTrigger)?,
)?;
right.set_callback({
let reference = reference.clone();
move |world| {
if let Some(window) = reference.upgrade() {
let mut tab = window.tab_mut();
let page = tab.downcast_mut::<P>();
page.next_tab(world)?;
}
Ok(())
}
});
Ok(Self {
reference,
base,
data: Vec::new(),
on_enable: Box::new(on_enable),
page: 0,
pages: 1,
ability_marker: PhantomData,
})
}
fn clear_grid(world: &mut World, grid: &Arc<Grid>) -> Result<()> {
let (rows, columns) = grid.dimensions();
for x in 0..columns {
for y in 0..rows {
grid.detach(world, x, y)?;
}
}
Ok(())
}
fn set_tab(&self, gui_handler: &mut GuiHandler, label: &Arc<Label>) -> Result<()> {
label.set_text(gui_handler, format!("{} / {}", self.page + 1, self.pages))
}
pub fn update_base<F>(&mut self, world: &mut World, setup: F) -> Result<()>
where
Self: ContentWrapper,
F: Fn(&mut World, &Arc<Button>, &T, usize) -> Result<()>,
{
self.refresh(world)?;
let grid: Arc<Grid> = self.base.element("content")?;
let label: Arc<Label> = self.base.element("tab_info")?;
Self::clear_grid(world, &grid)?;
self.set_tab(world.resources.get_mut()?, &label)?;
let (rows, columns) = grid.dimensions();
'outer: for y in 0..rows {
for x in 0..columns {
let index = (self.page * columns * rows) + y * columns + x;
match self.data.get(index) {
Some(t) => {
let snippet = GuiSnippet::from_str(
world,
include_str!("../resources/content_button.xml"),
)?;
let button: Arc<Button> = snippet.element("button")?;
setup(world, &button, t, index)?;
grid.attach(world, button, x, y, 1, 1)?;
}
None => break 'outer,
}
}
}
Ok(())
}
pub fn select(&self, gui_handler: &mut GuiHandler) -> Result<()> {
let grid: Arc<Grid> = self.base.element("content")?;
if let Some(child) = grid.child_at(0, 0)? {
child
.gridable()
.unwrap()
.selectable()
.unwrap()
.select(gui_handler)?;
}
Ok(())
}
}
impl<A: Ability + 'static, T: Send + Sync> ContentWrapper for Content<A, T>
where
Content<A, T>: ContentUpdate,
{
fn refresh(&mut self, world: &mut World) -> Result<()> {
self.data = (self.on_enable)(world)?;
let grid: Arc<Grid> = self.base.element("content")?;
let (rows, columns) = grid.dimensions();
self.pages = 1.max((self.data.len() as f32 / (rows * columns) as f32).ceil() as usize);
Ok(())
}
fn next_tab(&mut self, world: &mut World, hero: Entity) -> Result<()> {
if self.page < (self.pages - 1) {
self.page += 1;
self.update(world, hero)?;
}
Ok(())
}
fn previous_tab(&mut self, world: &mut World, hero: Entity) -> Result<()> {
if self.page > 0 {
self.page -= 1;
self.update(world, hero)?;
}
Ok(())
}
fn base(&self) -> &Arc<GuiSnippet> {
&self.base
}
fn is_empty(&self) -> bool {
self.data.is_empty()
}
}