use crate::Result;
use crate::GemGuiError;
use crate::JSMessageTx;
use crate::JSType;
use crate::element::Element;
use crate::element::Elements;
use crate::ui::Ui;
use crate::ui::private;
use crate::ui_data::ROOT_ID;
use crate::ui_data::UiDataRef;
use crate::ui_data::UiData;
pub struct UiRef {
ui: UiDataRef,
}
impl private::UserInterface for UiRef {
fn ui(&self) -> &UiDataRef {
&self.ui
}
}
impl Ui for UiRef {}
impl UiRef {
pub (crate) fn new(ui: UiDataRef) -> UiRef {
UiRef { ui }
}
pub async fn device_pixel_ratio(&self) -> Result<f32> {
let value = self.query("", "devicePixelRatio", &vec![]).await?;
if value.is_number() {
Ok(value.as_f64().unwrap() as f32)
} else {
GemGuiError::error(format!("Not a number {value}"))
}
}
pub async fn exists(&self, id: &str) -> Result<bool> {
let value = self.query(id, "exists", &vec!()).await?;
if value.is_boolean() {
Ok(value.as_bool().unwrap())
} else {
GemGuiError::error(format!("Not a bool {value}"))
}
}
pub async fn by_class(&self, class_name: &str) -> Result<Elements> {
let result = self.query(class_name, "classes", &vec![]).await;
match result {
Ok(value) => UiData::elements_from_values(&self.ui, value, &UiData::sender(&self.ui)),
Err(e) => Err(e),
}
}
pub async fn by_name<Str : Into<String>>(&self, name: Str) -> Result<Elements> {
let class_name = name.into();
let result = self.query(&class_name, "names", &vec![]).await;
match result {
Ok(value) => UiData::elements_from_values(&self.ui, value, &UiData::sender(&self.ui)),
Err(e) => Err(e),
}
}
fn contains_id(&self, key: &str) -> bool {
let ui = self.ui.lock().unwrap();
ui.elements.contains_key(key)
}
fn create_element(&self, id: &str, html_element: &str, parent: &Element) -> Result<Element> {
if id == ROOT_ID || self.contains_id(id) {
return GemGuiError::error("Bad id");
}
UiData::insert_element(&self.ui, id);
let ui = self.ui.clone();
let element = Element::construct(id.to_string(), UiData::sender(&ui), ui);
element.create(html_element, parent);
Ok(element)
}
pub fn add_element<OptCB, CB>(&self, html_element: &str, parent: &Element, element_ready: OptCB) -> Result<Element>
where
CB: FnMut(UiRef, Element) + Send + Clone + 'static,
OptCB: Into<Option<CB>> {
self.add_element_with_id::<OptCB, CB>(&UiData::random_element_id(&self.ui), html_element, parent, element_ready)
}
pub async fn add_element_async(&self, html_element: &str, parent: &Element) -> Result<Element> {
self.add_element_with_id_async(&UiData::random_element_id(&self.ui), html_element, parent).await
}
pub fn add_element_with_id<OptCB, CB>(&self, id: &str, html_element: &str, parent: &Element, element_ready: OptCB) -> Result<Element>
where
CB: FnMut(UiRef, Element) + Clone + Send + 'static,
OptCB: Into<Option<CB>> {
eprintln!("Element {} to create", &id);
let result = self.create_element(id, html_element, parent);
eprintln!("Element {} maybe created", &id);
match result {
Ok(element) => {
let inner = element.clone();
let cb = element_ready.into();
if let Some(mut f) = cb {
element.subscribe("created",
move |ui, _| {
f(ui, inner.clone())});
}
Ok(element)
},
Err(e) => Err(e)
}
}
pub async fn add_element_with_id_async(&self, id: &str, html_element: &str, parent: &Element) -> Result<Element> {
let element = self.create_element(id, html_element, parent);
let (tx, mut rx) = tokio::sync::mpsc::channel::<bool>(1);
if element.is_ok() {
let el = element.clone().unwrap();
el.subscribe_async("created",
|_, _| async move {
tx.send(true).await.expect("Fatal error");
});
}
match rx.recv().await {
Some(_) => element,
None => GemGuiError::error(format!("Element {id} not constructed"))
}
}
pub (crate) async fn query(&self, target: &str, name: &str, query_params: &Vec<String>) -> Result<JSType> {
Self::do_query(&self.ui, target, name, query_params).await
}
pub (crate) async fn do_query(ui: &UiDataRef, target: &str, name: &str, query_params: &Vec<String>) -> Result<JSType> {
let (id, receiver) = UiData::new_query(ui);
let msg = JSMessageTx {
element: target,
_type: "query",
query_id: Some(&id),
query: Some(name),
query_params: Some(query_params),
..Default::default()
};
UiData::send(ui, msg);
let value = tokio::task::spawn_blocking(move || {
receiver.blocking_recv()
}).await.unwrap_or_else(|e| {panic!("Query spawn blocking {e:#?}")});
match value {
Ok(v) => Ok(v),
Err(e) => GemGuiError::error(format!("Query error {e}"))
}
}
}