engine/character_window/src/content.rs

167 lines
4.4 KiB
Rust

use std::sync::{Arc, Weak};
use crate::*;
pub trait ContentWrapper: ContentUpdate + Send + Sync {
fn refresh(&mut self) -> Result<()>;
fn next_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()>;
fn previous_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()>;
fn base(&self) -> &Arc<GuiSnippet>;
fn is_empty(&self) -> bool;
}
pub trait ContentUpdate {
fn update(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()>;
fn select(&self) -> Result<()>;
}
pub struct Content<T: Send + Sync> {
pub reference: Weak<CharacterWindow>,
base: Arc<GuiSnippet>,
data: Vec<T>,
on_enable: Box<dyn Fn() -> Result<Vec<T>> + Send + Sync + 'static>,
page: usize,
pages: usize,
}
impl<T: Send + Sync> Content<T> {
pub fn new<F>(
engine: &Arc<Engine>,
reference: Weak<CharacterWindow>,
on_enable: F,
) -> Result<Self>
where
F: Fn() -> Result<Vec<T>> + Send + Sync + 'static,
{
let base = GuiSnippet::from_str(
engine.gui_handler(),
include_str!("../resources/content.xml"),
)?;
let left: Arc<Button> = base.element("left")?;
left.set_text("<")?;
left.set_info_icon(&engine.controller_icon(ControllerButton::LeftTrigger)?)?;
let right: Arc<Button> = base.element("right")?;
right.set_text(">")?;
right.set_info_icon(&engine.controller_icon(ControllerButton::RightTrigger)?)?;
Ok(Self {
reference,
base,
data: Vec::new(),
on_enable: Box::new(on_enable),
page: 0,
pages: 1,
})
}
fn clear_grid(grid: &Arc<Grid>) -> Result<()> {
let (rows, columns) = grid.dimensions();
for x in 0..columns {
for y in 0..rows {
grid.detach(x, y)?;
}
}
Ok(())
}
fn set_tab(&self, label: &Arc<Label>) -> Result<()> {
label.set_text(format!("{} / {}", self.page + 1, self.pages))
}
pub fn update_base<F>(&mut self, engine: &Arc<Engine>, setup: F) -> Result<()>
where
Self: ContentWrapper,
F: Fn(&Arc<Button>, &T, usize) -> Result<()>,
{
self.refresh()?;
let grid: Arc<Grid> = self.base.element("content")?;
let label: Arc<Label> = self.base.element("tab_info")?;
Self::clear_grid(&grid)?;
self.set_tab(&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(
engine.gui_handler(),
include_str!("../resources/content_button.xml"),
)?;
let button: Arc<Button> = snippet.element("button")?;
setup(&button, t, index)?;
grid.attach(button, x, y, 1, 1)?;
}
None => break 'outer,
}
}
}
Ok(())
}
pub fn select(&self) -> Result<()> {
let grid: Arc<Grid> = self.base.element("content")?;
if let Some(child) = grid.child_at(0, 0)? {
child.gridable().unwrap().selectable().unwrap().select()?;
}
Ok(())
}
}
impl<T: Send + Sync> ContentWrapper for Content<T>
where
Content<T>: ContentUpdate,
{
fn refresh(&mut self) -> Result<()> {
self.data = (self.on_enable)()?;
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, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
if self.page < (self.pages - 1) {
self.page += 1;
self.update(engine, hero)?;
}
Ok(())
}
fn previous_tab(&mut self, engine: &Arc<Engine>, hero: Entity) -> Result<()> {
if self.page > 0 {
self.page -= 1;
self.update(engine, hero)?;
}
Ok(())
}
fn base(&self) -> &Arc<GuiSnippet> {
&self.base
}
fn is_empty(&self) -> bool {
self.data.is_empty()
}
}