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