2023-01-16 09:53:52 +00:00
|
|
|
use std::{any::Any, collections::HashMap, sync::Arc};
|
|
|
|
|
|
|
|
use assetpath::AssetPath;
|
|
|
|
|
|
|
|
use super::validator::{
|
|
|
|
buttoninfo::NeighbourInfo,
|
|
|
|
uiinfoelement::UiInfoElement,
|
|
|
|
validator::{handle_function_suffix, Validator},
|
|
|
|
};
|
|
|
|
use crate::prelude::*;
|
|
|
|
use anyhow::Result;
|
|
|
|
|
|
|
|
pub struct GuiSnippet {
|
|
|
|
grid: Arc<Grid>,
|
|
|
|
|
|
|
|
ids: HashMap<String, UiElement>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuiSnippet {
|
2023-01-18 05:16:41 +00:00
|
|
|
pub fn new(gui_handler: &Arc<GuiHandler>, path: &AssetPath) -> Result<Arc<Self>> {
|
2023-01-17 06:07:19 +00:00
|
|
|
let validator = Validator::new(
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
&gui_handler,
|
|
|
|
path,
|
|
|
|
)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-18 05:16:41 +00:00
|
|
|
Self::_new(gui_handler, validator)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn from_str(gui_handler: &Arc<GuiHandler>, s: &str) -> Result<Arc<Self>> {
|
|
|
|
let validator = Validator::from_str(
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
gui_handler,
|
|
|
|
s,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
Self::_new(gui_handler, validator)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn _new(gui_handler: &Arc<GuiHandler>, validator: Validator) -> Result<Arc<Self>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let root = validator.root();
|
|
|
|
|
|
|
|
let mut ids = HashMap::new();
|
|
|
|
let mut custom_neighbours = Vec::new();
|
|
|
|
|
|
|
|
if root.children.len() != 1 {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
2023-01-18 05:16:41 +00:00
|
|
|
"Expected exact 1 root. Found {} roots",
|
2023-01-16 09:53:52 +00:00
|
|
|
root.children.len()
|
|
|
|
)));
|
|
|
|
}
|
|
|
|
|
|
|
|
let grid_info = &root.children[0];
|
|
|
|
let grid = Grid::try_from(grid_info, &gui_handler, false)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(&mut ids, &grid_info.id, &grid)?;
|
|
|
|
|
|
|
|
for child in grid_info.children.read().unwrap().iter() {
|
|
|
|
Self::create_child(&gui_handler, child, &grid, &mut ids, &mut custom_neighbours)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
GuiBuilder::connect_custom_neighbours(&ids, custom_neighbours)?;
|
|
|
|
|
|
|
|
Ok(Arc::new(GuiSnippet { grid, ids }))
|
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! impl_element {
|
|
|
|
($data_type:ident $(< $($lt:lifetime),+ >)?) => {
|
|
|
|
impl GetElement<$data_type$(<$($lt,)+>)?> for GuiSnippet {
|
|
|
|
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);
|
|
|
|
|
|
|
|
impl Visibility for GuiSnippet {
|
|
|
|
fn visible(&self) -> bool {
|
|
|
|
self.grid.visible()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_visibility(&self, visibility: bool) -> Result<()> {
|
|
|
|
self.grid.set_visibility(visibility)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GuiElementTraits for GuiSnippet {
|
|
|
|
fn gridable(&self) -> Option<&dyn Gridable> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visibility(&self) -> Option<&dyn Visibility> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
|
|
|
|
Some(GuiElement::GuiSnippet(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Gridable for GuiSnippet {
|
|
|
|
fn set_frame(
|
|
|
|
&self,
|
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
w: u32,
|
|
|
|
h: u32,
|
|
|
|
vert_align: VerticalAlign,
|
|
|
|
hori_align: HorizontalAlign,
|
|
|
|
) -> Result<()> {
|
|
|
|
self.grid.set_frame(x, y, w, h, vert_align, hori_align)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn selectable(&self) -> Option<&Arc<Selectable>> {
|
|
|
|
self.grid.selectable()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
"GuiSnippet"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_layer(&self, layer: i32) -> Result<()> {
|
|
|
|
self.grid.set_layer(layer)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Functionality for GuiSnippet {
|
|
|
|
fn set_click_callbacks(
|
|
|
|
&self,
|
|
|
|
functions: Vec<(&str, Box<dyn Fn() -> Result<()> + Send + Sync>)>,
|
|
|
|
) -> 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,
|
|
|
|
_functions: Vec<(&str, Box<dyn Fn(&dyn Any) -> Result<()> + Send + Sync>)>,
|
|
|
|
) -> Result<()> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_select_callbacks(
|
|
|
|
&self,
|
|
|
|
callbacks: Vec<(&str, Box<CustomCallback<bool, ()>>)>,
|
|
|
|
) -> 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,
|
|
|
|
callbacks: Vec<(&str, Box<CustomCallback<ControllerButton, bool>>)>,
|
|
|
|
) -> 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 GuiSnippet {
|
|
|
|
fn create_child(
|
|
|
|
gui_handler: &Arc<GuiHandler>,
|
|
|
|
child: &UiInfoElement,
|
|
|
|
grid: &Grid,
|
|
|
|
ids: &mut HashMap<String, UiElement>,
|
|
|
|
neighbour_infos: &mut Vec<(String, Vec<NeighbourInfo>)>,
|
|
|
|
) -> Result<()> {
|
|
|
|
match child {
|
|
|
|
UiInfoElement::Button(button_info) => {
|
|
|
|
let button = Button::try_from(button_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &button_info.id, &button)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
button.clone(),
|
|
|
|
button_info.x_slot.get()?,
|
|
|
|
button_info.y_slot.get()?,
|
|
|
|
button_info.x_dim,
|
|
|
|
button_info.y_dim,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
neighbour_infos.push((button_info.id.clone(), button_info.neighbour_infos.clone()));
|
|
|
|
}
|
|
|
|
UiInfoElement::Label(label_info) => {
|
|
|
|
let label = Label::try_from(label_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &label_info.id, &label)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
label,
|
|
|
|
label_info.x_slot.get()?,
|
|
|
|
label_info.y_slot.get()?,
|
|
|
|
label_info.x_dim,
|
|
|
|
label_info.y_dim,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UiInfoElement::MultiLineLabel(multi_line_label_info) => {
|
|
|
|
let multi_line_label =
|
|
|
|
MultiLineLabel::try_from(multi_line_label_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &multi_line_label_info.id, &multi_line_label)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
multi_line_label,
|
|
|
|
multi_line_label_info.x_slot.get()?,
|
|
|
|
multi_line_label_info.y_slot.get()?,
|
|
|
|
multi_line_label_info.x_dim,
|
|
|
|
multi_line_label_info.y_dim,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UiInfoElement::MultiLineTextField(multi_line_text_field_info) => {
|
|
|
|
let multi_line_text_field =
|
|
|
|
MultiLineTextField::try_from(multi_line_text_field_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &multi_line_text_field_info.id, &multi_line_text_field)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
multi_line_text_field,
|
|
|
|
multi_line_text_field_info.x_slot.get()?,
|
|
|
|
multi_line_text_field_info.y_slot.get()?,
|
|
|
|
multi_line_text_field_info.x_dim,
|
|
|
|
multi_line_text_field_info.y_dim,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UiInfoElement::TextField(text_field_info) => {
|
|
|
|
let text_field = TextField::try_from(text_field_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &text_field_info.id, &text_field)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
text_field,
|
|
|
|
text_field_info.x_slot.get()?,
|
|
|
|
text_field_info.y_slot.get()?,
|
|
|
|
text_field_info.x_dim,
|
|
|
|
text_field_info.y_dim,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UiInfoElement::Icon(icon_info) => {
|
|
|
|
let icon = Icon::try_from(icon_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &icon_info.id, UiElement::Icon(Arc::downgrade(&icon)))?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
icon,
|
|
|
|
icon_info.x_slot.get()?,
|
|
|
|
icon_info.y_slot.get()?,
|
|
|
|
icon_info.x_dim,
|
|
|
|
icon_info.y_dim,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UiInfoElement::ProgressBar(progress_bar_info) => {
|
|
|
|
let progress_bar = ProgressBar::try_from(progress_bar_info, gui_handler)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &progress_bar_info.id, &progress_bar)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
progress_bar,
|
|
|
|
progress_bar_info.x_slot.get()?,
|
|
|
|
progress_bar_info.y_slot.get()?,
|
|
|
|
progress_bar_info.x_dim,
|
|
|
|
progress_bar_info.y_dim,
|
|
|
|
)?;
|
|
|
|
}
|
|
|
|
UiInfoElement::Grid(grid_info) => {
|
|
|
|
let sub_grid = Grid::try_from(grid_info, gui_handler, false)?;
|
|
|
|
|
|
|
|
GuiBuilder::insert_id(ids, &grid_info.id, &sub_grid)?;
|
|
|
|
|
|
|
|
grid.attach(
|
|
|
|
sub_grid.clone(),
|
|
|
|
grid_info.x_slot.get()?,
|
|
|
|
grid_info.y_slot.get()?,
|
|
|
|
grid_info.x_dim,
|
|
|
|
grid_info.y_dim,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
for child in grid_info.children.read().unwrap().iter() {
|
|
|
|
Self::create_child(gui_handler, child, &sub_grid, ids, neighbour_infos)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|