//! `Selectable` is a property to select an item per button use super::executable::Executable; use crate::prelude::*; use anyhow::Result; use std::sync::{ Arc, RwLock, Weak, atomic::{AtomicBool, AtomicI32, Ordering::SeqCst}, }; /// `Selectable` gives the ability to navigate per button or controller to /// optionally adjacent neighbour Selectables and to execute a closure /// when the current Selectable is pressed pub struct Selectable<'a> { selected: AtomicBool, east_neighbour: RwLock>>>, west_neighbour: RwLock>>>, north_neighbour: RwLock>>>, south_neighbour: RwLock>>>, #[cfg(feature = "audio")] select_audible: RwLock>>, #[cfg(feature = "audio")] click_audible: RwLock>>, ui_layer: AtomicI32, isolate: bool, // used when clicked executable: Arc>, // used internally by button selected_changed_executable: Arc>, // exposed externally for event on_select_executable: Arc>, // used for custom buttons custom_callback: RwLock Result + Send + Sync>>>, } impl<'a> Selectable<'a> { /// Factory method for `Selectable`, returns `Arc`. /// /// # Arguments /// /// * `executable` is a `Arc` instance pub fn new( executable: Arc>, selected_changed_executable: Arc>, on_select_executable: Arc>, isolate: bool, ) -> Arc { Arc::new(Selectable { selected: AtomicBool::new(false), east_neighbour: RwLock::new(None), west_neighbour: RwLock::new(None), north_neighbour: RwLock::new(None), south_neighbour: RwLock::new(None), #[cfg(feature = "audio")] select_audible: RwLock::new(None), #[cfg(feature = "audio")] click_audible: RwLock::new(None), executable, selected_changed_executable, on_select_executable, custom_callback: RwLock::new(None), ui_layer: AtomicI32::new(0), isolate, }) } /// Add method, has to be explicitly called, otherwise it will remain in memory. /// /// # Arguments /// /// * `selectable` is a `&Arc` instance that is going to be added pub fn add(self: &Arc, gui_handler: &mut GuiHandler<'_>) -> Result<()> { gui_handler.add_selectable(self.ui_layer.load(SeqCst), self.clone()) } /// Delete method, has to be explicitly called, otherwise it will remain in memory. /// /// # Arguments /// /// * `selectable` is a `&Arc` instance that is going to be deleted pub fn delete(self: &Arc, gui_handler: &mut GuiHandler<'_>) -> Result<()> { gui_handler.delete_selectable(self.ui_layer.load(SeqCst), self)?; self.set_selected(gui_handler, false)?; Ok(()) } pub fn set_ui_layer(&self, ui_layer: i32) { self.ui_layer.store(ui_layer, SeqCst); } /// Selects this `Selectable` /// /// # Argument /// /// * `selectable` is a `Arc` instance pub fn select(self: &Arc, gui_handler: &mut GuiHandler<'_>) -> Result<()> { gui_handler.set_selectable(Some(self.clone())) } pub fn set_custom_callback(&self, custom_callback: F) where F: Fn(ControllerButton) -> Result + Send + Sync + 'static, { *self.custom_callback.write().unwrap() = Some(Box::new(custom_callback)); } #[cfg(feature = "audio")] pub fn set_select_audible(&self, audible: Option>) { *self.select_audible.write().unwrap() = audible; } #[cfg(feature = "audio")] pub fn set_click_audible(&self, audible: Option>) { *self.click_audible.write().unwrap() = audible; } /// Sets the value of selected and calls the callback if the value changed. /// Generally used by the `GuiHandler`. /// /// # Arguments /// /// * `selected` is the new selected state pub fn set_selected(&self, gui_handler: &mut GuiHandler<'_>, selected: bool) -> Result<()> { if self.selected() != selected { self.selected.store(selected, SeqCst); self.selected_changed_executable .execute(gui_handler, selected)?; self.on_select_executable.execute(gui_handler, selected)?; #[cfg(feature = "audio")] { if self.selected() { if let Some(audible) = self.select_audible.read().unwrap().as_ref() { audible.play()?; } } } } Ok(()) } /// Returns the selected state. Generally used by the `GuiHandler`. pub fn selected(&self) -> bool { self.selected.load(SeqCst) } /// Executes the `Executable`'s callback pub(crate) fn click_event(&self, gui_handler: &mut GuiHandler<'_>) -> Result<()> { #[cfg(feature = "audio")] { if let Some(audible) = self.click_audible.read().unwrap().as_ref() { audible.play()?; } } self.executable.execute(gui_handler, ())?; Ok(()) } pub(crate) fn custom_click_event(&self, button: ControllerButton) -> Result { if let Some(custom_callback) = self.custom_callback.read().unwrap().as_ref() { if custom_callback(button)? { #[cfg(feature = "audio")] { if let Some(audible) = self.click_audible.read().unwrap().as_ref() { audible.play()?; } } return Ok(true); } } Ok(false) } /// Returns the current east neighbour, if possible pub fn east_neighbour(&self) -> Option>> { if let Some(weak_neighbour) = self.east_neighbour.read().unwrap().as_ref() { if let Some(neighbour) = weak_neighbour.upgrade() { return Some(neighbour); } } None } /// Replaces the current east neighbour /// /// # Arguments /// /// * `selectable` the new east neighbour pub fn set_east_neighbour(&self, selectable: Option<&Arc>>) { if self.isolate { return; } let mut east_neighbour = self.east_neighbour.write().unwrap(); match selectable { Some(selectable) => *east_neighbour = Some(Arc::downgrade(selectable)), None => *east_neighbour = None, } } /// Returns the current west neighbour, if possible pub fn west_neighbour(&self) -> Option>> { if let Some(weak_neighbour) = self.west_neighbour.read().unwrap().as_ref() { if let Some(neighbour) = weak_neighbour.upgrade() { return Some(neighbour); } } None } /// Replaces the current west neighbour /// /// # Arguments /// /// * `selectable` the new west neighbour pub fn set_west_neighbour(&self, selectable: Option<&Arc>>) { if self.isolate { return; } let mut west_neighbour = self.west_neighbour.write().unwrap(); match selectable { Some(selectable) => *west_neighbour = Some(Arc::downgrade(selectable)), None => *west_neighbour = None, } } /// Returns the current north neighbour, if possible pub fn north_neighbour(&self) -> Option>> { if let Some(weak_neighbour) = self.north_neighbour.read().unwrap().as_ref() { if let Some(neighbour) = weak_neighbour.upgrade() { return Some(neighbour); } } None } /// Replaces the current north neighbour /// /// # Argumnents /// /// * `selectable` the new north neighbour pub fn set_north_neighbour(&self, selectable: Option<&Arc>>) { if self.isolate { return; } let mut north_neighbour = self.north_neighbour.write().unwrap(); match selectable { Some(selectable) => *north_neighbour = Some(Arc::downgrade(selectable)), None => *north_neighbour = None, } } /// Returns the current south neighbour, if possible pub fn south_neighbour(&self) -> Option>> { if let Some(weak_neighbour) = self.south_neighbour.read().unwrap().as_ref() { if let Some(neighbour) = weak_neighbour.upgrade() { return Some(neighbour); } } None } /// Replaces the current south neighbour /// /// # Arguments /// /// * `selectable` the new south neighbour pub fn set_south_neighbour(&self, selectable: Option<&Arc>>) { if self.isolate { return; } let mut south_neighbour = self.south_neighbour.write().unwrap(); match selectable { Some(selectable) => *south_neighbour = Some(Arc::downgrade(selectable)), None => *south_neighbour = None, } } pub fn connect_vertically<'c>(upper: &Arc>, lower: &Arc>) { if upper.isolate || lower.isolate { return; } *upper.south_neighbour.write().unwrap() = Some(Arc::downgrade(lower)); *lower.north_neighbour.write().unwrap() = Some(Arc::downgrade(upper)); } pub fn connect_horizontally<'c>(left: &Arc>, right: &Arc>) { if left.isolate || right.isolate { return; } *left.east_neighbour.write().unwrap() = Some(Arc::downgrade(right)); *right.west_neighbour.write().unwrap() = Some(Arc::downgrade(left)); } }