ui/src/builder/snippet.rs

318 lines
9.5 KiB
Rust
Raw Normal View History

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(())
}
}