2023-01-16 09:53:52 +00:00
|
|
|
use crate::prelude::*;
|
2025-03-04 13:25:35 +00:00
|
|
|
use anyhow::{Context, Result, bail};
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
use assetpath::AssetPath;
|
2025-04-05 08:24:07 +00:00
|
|
|
use ecs::*;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
use super::validator::buttoninfo::{NeighbourDirection, NeighbourInfo};
|
|
|
|
use super::validator::gridinfo::GridInfo;
|
|
|
|
use super::validator::uiinfoelement::UiInfoElement;
|
2025-03-04 13:25:35 +00:00
|
|
|
use super::validator::validator::{Validator, handle_function_suffix};
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
use std::any::Any;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
|
|
|
|
pub struct GuiBuilder {
|
|
|
|
grids: Vec<Arc<Grid>>,
|
|
|
|
default_select: Option<Arc<Button>>,
|
|
|
|
|
|
|
|
ids: HashMap<String, UiElement>,
|
|
|
|
|
|
|
|
decline_callback: RwLock<Option<Box<dyn Fn() -> Result<()> + Send + Sync>>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuiBuilder {
|
2025-03-05 06:59:38 +00:00
|
|
|
pub fn new(world: &mut World, path: &AssetPath) -> Result<Arc<Self>> {
|
2023-01-17 06:07:19 +00:00
|
|
|
let validator = Validator::new(
|
|
|
|
#[cfg(feature = "audio")]
|
2025-03-05 07:04:19 +00:00
|
|
|
world.resources.get::<GuiHandler>(),
|
2023-01-17 06:07:19 +00:00
|
|
|
path,
|
2024-04-07 13:39:32 +00:00
|
|
|
)
|
|
|
|
.with_context(|| format!("validator for {}", path.full_path()))?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
Ok(Arc::new(Self::_new(world, validator).with_context(
|
2024-04-07 13:39:32 +00:00
|
|
|
|| format!("for file {}", path.full_path()),
|
|
|
|
)?))
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
pub fn from_str(world: &mut World, s: &str) -> Result<Arc<Self>> {
|
2023-01-17 06:07:19 +00:00
|
|
|
let validator = Validator::from_str(
|
|
|
|
#[cfg(feature = "audio")]
|
2025-03-05 07:04:19 +00:00
|
|
|
world.resources.get::<GuiHandler>(),
|
2023-01-17 06:07:19 +00:00
|
|
|
s,
|
|
|
|
)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
Ok(Arc::new(Self::_new(world, validator)?))
|
2024-03-28 07:32:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn merge<'a>(
|
2025-03-05 06:59:38 +00:00
|
|
|
world: &mut World,
|
2024-03-28 07:32:06 +00:00
|
|
|
pathes: impl IntoIterator<Item = &'a AssetPath>,
|
|
|
|
) -> Result<Arc<Self>> {
|
|
|
|
let mut me = Self {
|
|
|
|
grids: Default::default(),
|
|
|
|
default_select: Default::default(),
|
|
|
|
|
|
|
|
ids: Default::default(),
|
|
|
|
|
|
|
|
decline_callback: Default::default(),
|
|
|
|
};
|
|
|
|
|
|
|
|
for path in pathes.into_iter() {
|
|
|
|
let validator = Validator::new(
|
|
|
|
#[cfg(feature = "audio")]
|
2025-03-05 07:04:19 +00:00
|
|
|
world.resources.get::<GuiHandler>(),
|
2024-03-28 07:32:06 +00:00
|
|
|
path,
|
|
|
|
)?;
|
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
let builder = Self::_new(world, validator)
|
2024-04-07 13:39:32 +00:00
|
|
|
.with_context(|| format!("for file {}", path.full_path()))?;
|
2024-03-28 07:32:06 +00:00
|
|
|
|
|
|
|
me.grids.extend(builder.grids);
|
|
|
|
|
|
|
|
if me.default_select.is_some() && builder.default_select.is_some() {
|
|
|
|
bail!("multiple default selects are not supported!");
|
|
|
|
}
|
|
|
|
|
|
|
|
if builder.default_select.is_some() {
|
|
|
|
me.default_select = builder.default_select;
|
|
|
|
}
|
|
|
|
|
|
|
|
me.ids.extend(builder.ids);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Arc::new(me))
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2025-03-05 06:59:38 +00:00
|
|
|
fn _new(world: &mut World, validator: Validator) -> Result<Self> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let root = validator.root();
|
|
|
|
|
|
|
|
let mut ids = HashMap::new();
|
|
|
|
let mut opt_default_select = None;
|
|
|
|
let mut custom_neighbours = Vec::new();
|
|
|
|
|
|
|
|
let mut grids = Vec::new();
|
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
let gui_handler = world.resources.get_mut_unchecked::<GuiHandler>();
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
for child in &root.children {
|
|
|
|
let tree = Self::create_tree(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2023-01-16 09:53:52 +00:00
|
|
|
gui_handler,
|
|
|
|
&mut ids,
|
|
|
|
child,
|
|
|
|
&mut opt_default_select,
|
|
|
|
&mut custom_neighbours,
|
|
|
|
validator.dimensions(),
|
2024-05-16 06:45:43 +00:00
|
|
|
validator.layer().unwrap_or(5),
|
2023-01-16 09:53:52 +00:00
|
|
|
)?;
|
|
|
|
|
|
|
|
grids.push(tree);
|
|
|
|
}
|
|
|
|
|
|
|
|
Self::connect_custom_neighbours(&ids, custom_neighbours)?;
|
|
|
|
|
2024-03-28 07:32:06 +00:00
|
|
|
Ok(GuiBuilder {
|
2023-01-16 09:53:52 +00:00
|
|
|
grids,
|
|
|
|
default_select: opt_default_select,
|
|
|
|
|
|
|
|
ids,
|
|
|
|
|
|
|
|
decline_callback: RwLock::new(None),
|
2024-03-28 07:32:06 +00:00
|
|
|
})
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn connect_custom_neighbours(
|
|
|
|
ids: &HashMap<String, UiElement>,
|
|
|
|
neighbour_infos: Vec<(String, Vec<NeighbourInfo>)>,
|
|
|
|
) -> Result<()> {
|
|
|
|
for (id, neighbour_infos) in neighbour_infos.iter() {
|
|
|
|
let button: Arc<Button> = ids.element(id)?;
|
|
|
|
|
|
|
|
for neighbour_info in neighbour_infos.iter() {
|
|
|
|
let neighbour: Arc<Button> = ids.element(&neighbour_info.id)?;
|
|
|
|
let neighbour_selectable = neighbour.selectable();
|
|
|
|
|
|
|
|
button
|
|
|
|
.selectable()
|
|
|
|
.map(|selectable| match neighbour_info.direction {
|
|
|
|
NeighbourDirection::East => {
|
|
|
|
selectable.set_east_neighbour(neighbour_selectable)
|
|
|
|
}
|
|
|
|
NeighbourDirection::West => {
|
|
|
|
selectable.set_west_neighbour(neighbour_selectable)
|
|
|
|
}
|
|
|
|
NeighbourDirection::North => {
|
|
|
|
selectable.set_north_neighbour(neighbour_selectable)
|
|
|
|
}
|
|
|
|
NeighbourDirection::South => {
|
|
|
|
selectable.set_south_neighbour(neighbour_selectable)
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_decline_callback<F>(&self, decline_callback: F)
|
|
|
|
where
|
|
|
|
F: Fn() -> Result<()> + 'static + Send + Sync,
|
|
|
|
{
|
|
|
|
*self.decline_callback.write().unwrap() = Some(Box::new(decline_callback));
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_ui_layer(&self, layer: i32) -> Result<()> {
|
|
|
|
for grid in self.grids.iter() {
|
|
|
|
grid.set_layer(layer)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-04-04 12:48:41 +00:00
|
|
|
|
|
|
|
pub fn ids(&self) -> Vec<&str> {
|
|
|
|
self.ids.keys().map(|n| n.as_str()).collect()
|
|
|
|
}
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// --- traits ---
|
|
|
|
|
|
|
|
impl Functionality for GuiBuilder {
|
|
|
|
fn set_click_callbacks(
|
|
|
|
&self,
|
2025-03-04 17:18:08 +00:00
|
|
|
functions: Vec<(&str, Box<dyn Fn(&mut World) -> Result<()> + Send + Sync>)>,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
for (function_name, callback) in functions {
|
|
|
|
let suffix_less_function_name = handle_function_suffix(function_name);
|
|
|
|
|
|
|
|
let button: Arc<Button> = self.element(&suffix_less_function_name)?;
|
|
|
|
button.set_callback(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_vec_callbacks(
|
|
|
|
&self,
|
2025-03-04 17:18:08 +00:00
|
|
|
_functions: Vec<(
|
|
|
|
&str,
|
|
|
|
Box<dyn Fn(&mut World, &dyn Any) -> Result<()> + Send + Sync>,
|
|
|
|
)>,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_select_callbacks(
|
|
|
|
&self,
|
2025-03-04 17:18:08 +00:00
|
|
|
callbacks: Vec<(
|
|
|
|
&str,
|
|
|
|
Box<dyn Fn(&mut World, bool) -> Result<()> + Send + Sync>,
|
|
|
|
)>,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
for (function_name, callback) in callbacks {
|
|
|
|
let suffix_less_function_name = handle_function_suffix(function_name);
|
|
|
|
|
|
|
|
let button: Arc<Button> = self.element(&suffix_less_function_name)?;
|
|
|
|
button.set_select_callback(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_custom_callbacks(
|
|
|
|
&self,
|
2025-03-04 17:18:08 +00:00
|
|
|
callbacks: Vec<(
|
|
|
|
&str,
|
|
|
|
Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>,
|
|
|
|
)>,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
for (function_name, callback) in callbacks {
|
|
|
|
let suffix_less_function_name = handle_function_suffix(function_name);
|
|
|
|
|
|
|
|
let button: Arc<Button> = self.element(&suffix_less_function_name)?;
|
|
|
|
button.set_custom_callback(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Visibility for GuiBuilder {
|
|
|
|
fn visible(&self) -> bool {
|
|
|
|
// assume that all grids are visible when the first one is visible
|
|
|
|
self.grids
|
|
|
|
.get(0)
|
|
|
|
.map(|grid| grid.visible())
|
|
|
|
.unwrap_or(false)
|
|
|
|
}
|
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
fn set_visibility(&self, world: &mut World, visibility: bool) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
for grid in &self.grids {
|
2025-03-05 06:59:38 +00:00
|
|
|
grid.set_visibility(world, visibility)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuiElementTraits for GuiBuilder {
|
|
|
|
fn gridable(&self) -> Option<&dyn Gridable> {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visibility(&self) -> Option<&dyn Visibility> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
|
|
|
|
Some(GuiElement::GuiBuilder(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TopGui for GuiBuilder {
|
2025-03-04 17:18:08 +00:00
|
|
|
fn decline(&self, _world: &mut World) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
if let Some(decline_callback) = self.decline_callback.read().unwrap().as_ref() {
|
|
|
|
decline_callback()?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 17:18:08 +00:00
|
|
|
fn next_tab(&self, _world: &mut World, _: bool) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 17:18:08 +00:00
|
|
|
fn previous_tab(&self, _world: &mut World, _: bool) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TopLevelGui for GuiBuilder {
|
|
|
|
fn gui_traits(&self) -> &dyn GuiElementTraits {
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
fn top_gui(&self) -> Option<&dyn TopGui> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn elements(&self) -> Option<&HashMap<String, UiElement>> {
|
|
|
|
Some(&self.ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn functionality(&self) -> Option<&dyn Functionality> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
fn enable(&self, world: &mut World) -> Result<()> {
|
|
|
|
self.set_visibility(world, true)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(button) = &self.default_select {
|
2025-04-05 08:24:07 +00:00
|
|
|
button.select(world.resources.get_mut()?)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-05 06:59:38 +00:00
|
|
|
fn disable(&self, world: &mut World) -> Result<()> {
|
|
|
|
self.set_visibility(world, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_element {
|
|
|
|
($data_type:ident $(< $($lt:lifetime),+ >)? ) => {
|
|
|
|
impl GetElement<$data_type$(<$($lt,)+>)?> for GuiBuilder {
|
|
|
|
fn element(&self, id: &str) -> Result<Arc<$data_type$(<$($lt,)+>)?>> {
|
|
|
|
self.ids.element(id)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
impl_element!(Button);
|
|
|
|
impl_element!(Grid);
|
|
|
|
impl_element!(Label);
|
|
|
|
impl_element!(TextField);
|
|
|
|
impl_element!(Icon);
|
|
|
|
impl_element!(ProgressBar);
|
|
|
|
impl_element!(MultiLineLabel);
|
|
|
|
impl_element!(MultiLineTextField);
|
|
|
|
|
|
|
|
// private
|
|
|
|
impl GuiBuilder {
|
|
|
|
fn create_tree(
|
2025-03-05 06:59:38 +00:00
|
|
|
world: &mut World,
|
2025-03-04 13:25:35 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2023-01-16 09:53:52 +00:00
|
|
|
ids: &mut HashMap<String, UiElement>,
|
|
|
|
root_grid_info: &Arc<GridInfo>,
|
|
|
|
default_button: &mut Option<Arc<Button>>,
|
|
|
|
neighbour_infos: &mut Vec<(String, Vec<NeighbourInfo>)>,
|
|
|
|
(reference_width, reference_height): (Option<u32>, Option<u32>),
|
2024-05-16 06:45:43 +00:00
|
|
|
layer: i32,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<Arc<Grid>> {
|
2025-03-04 13:25:35 +00:00
|
|
|
let root_grid = Grid::try_from(gui_handler, root_grid_info, true)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
let root_layer = root_grid_info.layer.unwrap_or(layer);
|
|
|
|
root_grid.set_layer(root_layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(ref_width) = reference_width {
|
|
|
|
if let Some(ref_height) = reference_height {
|
|
|
|
root_grid
|
|
|
|
.framable
|
2025-03-04 13:25:35 +00:00
|
|
|
.set_reference_size(gui_handler, ref_width, ref_height)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
root_grid.set_frame(
|
2025-03-04 13:25:35 +00:00
|
|
|
gui_handler,
|
2024-04-07 13:39:32 +00:00
|
|
|
root_grid_info
|
|
|
|
.x_offset
|
|
|
|
.get()
|
|
|
|
.with_context(|| "x_offset of root grid")?,
|
|
|
|
root_grid_info
|
|
|
|
.y_offset
|
|
|
|
.get()
|
|
|
|
.with_context(|| "y_offset of root grid")?,
|
|
|
|
root_grid_info
|
|
|
|
.width
|
|
|
|
.get()
|
|
|
|
.with_context(|| "width of root grid")?,
|
|
|
|
root_grid_info
|
|
|
|
.height
|
|
|
|
.get()
|
|
|
|
.with_context(|| "height of root grid")?,
|
|
|
|
root_grid_info
|
|
|
|
.vertical_alignment
|
|
|
|
.get()
|
|
|
|
.with_context(|| "vertical alignment of root grid")?,
|
|
|
|
root_grid_info
|
|
|
|
.horizontal_alignment
|
|
|
|
.get()
|
|
|
|
.with_context(|| "horizontal alignment of root grid")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
)?;
|
|
|
|
|
|
|
|
Self::insert_id(ids, &root_grid_info.id, &root_grid)?;
|
|
|
|
|
|
|
|
for child in root_grid_info.children.read().unwrap().iter() {
|
|
|
|
Self::create_child(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2023-01-16 09:53:52 +00:00
|
|
|
gui_handler,
|
|
|
|
child,
|
|
|
|
&root_grid,
|
|
|
|
ids,
|
|
|
|
default_button,
|
|
|
|
neighbour_infos,
|
2024-05-16 06:45:43 +00:00
|
|
|
root_layer,
|
2023-01-16 09:53:52 +00:00
|
|
|
)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(root_grid)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_child(
|
2025-03-05 06:59:38 +00:00
|
|
|
world: &mut World,
|
2025-03-04 13:25:35 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2023-01-16 09:53:52 +00:00
|
|
|
child: &UiInfoElement,
|
|
|
|
grid: &Grid,
|
|
|
|
ids: &mut HashMap<String, UiElement>,
|
|
|
|
default_button: &mut Option<Arc<Button>>,
|
|
|
|
neighbour_infos: &mut Vec<(String, Vec<NeighbourInfo>)>,
|
2024-05-16 06:45:43 +00:00
|
|
|
layer: i32,
|
2023-01-16 09:53:52 +00:00
|
|
|
) -> Result<()> {
|
|
|
|
match child {
|
|
|
|
UiInfoElement::Button(button_info) => {
|
2025-03-04 13:25:35 +00:00
|
|
|
let button = Button::try_from(gui_handler, button_info)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Self::insert_id(ids, &button_info.id, &button)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2023-01-16 09:53:52 +00:00
|
|
|
button.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
button_info
|
|
|
|
.x_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "x_slot of button")?,
|
|
|
|
button_info
|
|
|
|
.y_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "y_slot of button")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
button_info.x_dim,
|
|
|
|
button_info.y_dim,
|
|
|
|
)?;
|
|
|
|
|
2024-05-16 06:45:43 +00:00
|
|
|
button.set_layer(layer)?;
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
neighbour_infos.push((button_info.id.clone(), button_info.neighbour_infos.clone()));
|
|
|
|
|
|
|
|
if button_info.select {
|
|
|
|
match default_button {
|
|
|
|
Some(_) => {
|
|
|
|
return Err(anyhow::Error::msg(
|
|
|
|
"It is not allowed to select multiple UI elements",
|
2025-03-04 13:25:35 +00:00
|
|
|
));
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
None => *default_button = Some(button.clone()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
UiInfoElement::Label(label_info) => {
|
|
|
|
let label = Label::try_from(label_info, gui_handler)?;
|
|
|
|
|
|
|
|
Self::insert_id(ids, &label_info.id, &label)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2024-05-16 06:45:43 +00:00
|
|
|
label.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
label_info.x_slot.get().with_context(|| "x_slot of label")?,
|
|
|
|
label_info.y_slot.get().with_context(|| "y_slot of label")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
label_info.x_dim,
|
|
|
|
label_info.y_dim,
|
|
|
|
)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
|
|
|
|
label.set_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
UiInfoElement::MultiLineLabel(multi_line_label_info) => {
|
|
|
|
let multi_line_label =
|
2025-03-05 06:59:38 +00:00
|
|
|
MultiLineLabel::try_from(multi_line_label_info, world, gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &multi_line_label_info.id, &multi_line_label)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2024-05-16 06:45:43 +00:00
|
|
|
multi_line_label.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
multi_line_label_info
|
|
|
|
.x_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "x_slot of label")?,
|
|
|
|
multi_line_label_info
|
|
|
|
.y_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "y_slot of label")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
multi_line_label_info.x_dim,
|
|
|
|
multi_line_label_info.y_dim,
|
|
|
|
)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
|
|
|
|
multi_line_label.set_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
UiInfoElement::MultiLineTextField(multi_line_text_field_info) => {
|
|
|
|
let multi_line_text_field =
|
2025-03-05 06:59:38 +00:00
|
|
|
MultiLineTextField::try_from(multi_line_text_field_info, world, gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &multi_line_text_field_info.id, &multi_line_text_field)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2024-05-16 06:45:43 +00:00
|
|
|
multi_line_text_field.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
multi_line_text_field_info
|
|
|
|
.x_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "x_slot of text field")?,
|
|
|
|
multi_line_text_field_info
|
|
|
|
.y_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "y_slot of text field")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
multi_line_text_field_info.x_dim,
|
|
|
|
multi_line_text_field_info.y_dim,
|
|
|
|
)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
|
|
|
|
multi_line_text_field.set_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
UiInfoElement::TextField(text_field_info) => {
|
|
|
|
let text_field = TextField::try_from(text_field_info, gui_handler)?;
|
|
|
|
|
|
|
|
Self::insert_id(ids, &text_field_info.id, &text_field)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2024-05-16 06:45:43 +00:00
|
|
|
text_field.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
text_field_info
|
|
|
|
.x_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "x_slot of text field")?,
|
|
|
|
text_field_info
|
|
|
|
.y_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "y_slot of text field")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
text_field_info.x_dim,
|
|
|
|
text_field_info.y_dim,
|
|
|
|
)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
|
|
|
|
text_field.set_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
UiInfoElement::Icon(icon_info) => {
|
|
|
|
let icon = Icon::try_from(icon_info, gui_handler)?;
|
|
|
|
|
|
|
|
Self::insert_id(ids, &icon_info.id, UiElement::Icon(Arc::downgrade(&icon)))?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2024-05-16 06:45:43 +00:00
|
|
|
icon.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
icon_info.x_slot.get().with_context(|| "x_slot of icon")?,
|
|
|
|
icon_info.y_slot.get().with_context(|| "y_slot of icon")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
icon_info.x_dim,
|
|
|
|
icon_info.y_dim,
|
|
|
|
)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
|
|
|
|
icon.set_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
UiInfoElement::ProgressBar(progress_bar_info) => {
|
|
|
|
let progress_bar = ProgressBar::try_from(progress_bar_info, gui_handler)?;
|
|
|
|
|
|
|
|
Self::insert_id(ids, &progress_bar_info.id, &progress_bar)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2024-05-16 06:45:43 +00:00
|
|
|
progress_bar.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
progress_bar_info
|
|
|
|
.x_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "x_slot of progress bar")?,
|
|
|
|
progress_bar_info
|
|
|
|
.y_slot
|
|
|
|
.get()
|
|
|
|
.with_context(|| "y_slot of progress bar")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
progress_bar_info.x_dim,
|
|
|
|
progress_bar_info.y_dim,
|
|
|
|
)?;
|
2024-05-16 06:45:43 +00:00
|
|
|
|
|
|
|
progress_bar.set_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
UiInfoElement::Grid(grid_info) => {
|
2025-03-04 13:25:35 +00:00
|
|
|
let sub_grid = Grid::try_from(gui_handler, grid_info, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Self::insert_id(ids, &grid_info.id, &sub_grid)?;
|
|
|
|
|
|
|
|
grid.attach(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2023-01-16 09:53:52 +00:00
|
|
|
sub_grid.clone(),
|
2024-04-07 13:39:32 +00:00
|
|
|
grid_info.x_slot.get().with_context(|| "x_slot of grid")?,
|
|
|
|
grid_info.y_slot.get().with_context(|| "y_slot of grid")?,
|
2023-01-16 09:53:52 +00:00
|
|
|
grid_info.x_dim,
|
|
|
|
grid_info.y_dim,
|
|
|
|
)?;
|
|
|
|
|
2024-05-16 06:45:43 +00:00
|
|
|
let sub_grid_layer = grid_info.layer.unwrap_or(layer);
|
|
|
|
sub_grid.set_layer(sub_grid_layer)?;
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
for child in grid_info.children.read().unwrap().iter() {
|
|
|
|
Self::create_child(
|
2025-03-05 06:59:38 +00:00
|
|
|
world,
|
2023-01-16 09:53:52 +00:00
|
|
|
gui_handler,
|
|
|
|
child,
|
|
|
|
&sub_grid,
|
|
|
|
ids,
|
|
|
|
default_button,
|
|
|
|
neighbour_infos,
|
2024-05-16 06:45:43 +00:00
|
|
|
sub_grid_layer,
|
2023-01-16 09:53:52 +00:00
|
|
|
)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn insert_id(
|
|
|
|
ids: &mut HashMap<String, UiElement>,
|
|
|
|
id: &String,
|
|
|
|
element: impl Into<UiElement>,
|
|
|
|
) -> Result<()> {
|
|
|
|
if !id.is_empty() {
|
|
|
|
if let Some(_) = ids.insert(id.clone(), element.into()) {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
|
|
|
"ID ({}) is used multiple times",
|
|
|
|
id,
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|