ui/src/gui_handler/gui/selectable.rs

331 lines
10 KiB
Rust
Raw Normal View History

2023-01-16 09:53:52 +00:00
//! `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},
2023-01-16 09:53:52 +00:00
};
/// `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
2025-03-03 19:01:17 +00:00
pub struct Selectable<'a> {
2023-01-16 09:53:52 +00:00
selected: AtomicBool,
east_neighbour: RwLock<Option<Weak<Selectable<'a>>>>,
west_neighbour: RwLock<Option<Weak<Selectable<'a>>>>,
north_neighbour: RwLock<Option<Weak<Selectable<'a>>>>,
south_neighbour: RwLock<Option<Weak<Selectable<'a>>>>,
2023-01-16 09:53:52 +00:00
#[cfg(feature = "audio")]
select_audible: RwLock<Option<Arc<Audible>>>,
#[cfg(feature = "audio")]
click_audible: RwLock<Option<Arc<Audible>>>,
ui_layer: AtomicI32,
2024-05-17 13:12:48 +00:00
isolate: bool,
2023-01-16 09:53:52 +00:00
// used when clicked
2025-03-03 19:01:17 +00:00
executable: Arc<Executable<&'a mut World>>,
2023-01-16 09:53:52 +00:00
// used internally by button
2025-03-03 19:01:17 +00:00
selected_changed_executable: Arc<Executable<(&'a mut World, bool)>>,
2023-01-16 09:53:52 +00:00
// exposed externally for event
on_select_executable: Arc<Executable<(&'a mut World, bool)>>,
2023-01-16 09:53:52 +00:00
// used for custom buttons
custom_callback:
RwLock<Option<Box<dyn Fn(&mut World, ControllerButton) -> Result<bool> + Send + Sync>>>,
2023-01-16 09:53:52 +00:00
}
2025-03-03 19:01:17 +00:00
impl<'a> Selectable<'a> {
2023-01-16 09:53:52 +00:00
/// Factory method for `Selectable`, returns `Arc<Selectable>`.
///
/// # Arguments
///
/// * `executable` is a `Arc<Executable>` instance
pub fn new(
executable: Arc<Executable<&'a mut World>>,
selected_changed_executable: Arc<Executable<(&'a mut World, bool)>>,
on_select_executable: Arc<Executable<(&'a mut World, bool)>>,
2024-05-17 13:12:48 +00:00
isolate: bool,
2023-01-16 09:53:52 +00:00
) -> Arc<Self> {
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),
2024-05-17 13:12:48 +00:00
isolate,
2023-01-16 09:53:52 +00:00
})
}
/// Add method, has to be explicitly called, otherwise it will remain in memory.
///
/// # Arguments
///
/// * `selectable` is a `&Arc<Selectable>` instance that is going to be added
pub fn add(self: &Arc<Self>, gui_handler: &mut GuiHandler<'_>) -> Result<()> {
gui_handler.add_selectable(self.ui_layer.load(SeqCst), self.clone())
2023-01-16 09:53:52 +00:00
}
/// Delete method, has to be explicitly called, otherwise it will remain in memory.
///
/// # Arguments
///
/// * `selectable` is a `&Arc<Selectable>` instance that is going to be deleted
pub fn delete(self: &Arc<Self>, gui_handler: &mut GuiHandler<'_>) -> Result<()> {
gui_handler.delete_selectable(self.ui_layer.load(SeqCst), self)?;
self.set_selected(gui_handler, false)?;
2023-01-16 09:53:52 +00:00
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<Selectable>` instance
pub fn select(self: &Arc<Self>, gui_handler: &mut GuiHandler<'_>) -> Result<()> {
gui_handler.set_selectable(Some(self.clone()))
2023-01-16 09:53:52 +00:00
}
pub fn set_custom_callback<F>(&self, custom_callback: F)
where
F: Fn(ControllerButton) -> Result<bool> + Send + Sync + 'static,
{
*self.custom_callback.write().unwrap() = Some(Box::new(custom_callback));
}
#[cfg(feature = "audio")]
pub fn set_select_audible(&self, audible: Option<Arc<Audible>>) {
*self.select_audible.write().unwrap() = audible;
}
#[cfg(feature = "audio")]
pub fn set_click_audible(&self, audible: Option<Arc<Audible>>) {
*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<()> {
2023-01-16 09:53:52 +00:00
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)?;
2023-01-16 09:53:52 +00:00
#[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<()> {
2023-01-16 09:53:52 +00:00
#[cfg(feature = "audio")]
{
if let Some(audible) = self.click_audible.read().unwrap().as_ref() {
audible.play()?;
}
}
self.executable.execute(gui_handler, ())?;
2023-01-16 09:53:52 +00:00
Ok(())
}
pub(crate) fn custom_click_event(&self, button: ControllerButton) -> Result<bool> {
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<Arc<Selectable<'a>>> {
2023-01-16 09:53:52 +00:00
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<Selectable<'a>>>) {
2024-05-17 13:12:48 +00:00
if self.isolate {
return;
}
2023-01-16 09:53:52 +00:00
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<Arc<Selectable<'a>>> {
2023-01-16 09:53:52 +00:00
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<Selectable<'a>>>) {
2024-05-17 13:12:48 +00:00
if self.isolate {
return;
}
2023-01-16 09:53:52 +00:00
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<Arc<Selectable<'a>>> {
2023-01-16 09:53:52 +00:00
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<Selectable<'a>>>) {
2024-05-17 13:12:48 +00:00
if self.isolate {
return;
}
2023-01-16 09:53:52 +00:00
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<Arc<Selectable<'a>>> {
2023-01-16 09:53:52 +00:00
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<Selectable<'a>>>) {
2024-05-17 13:12:48 +00:00
if self.isolate {
return;
}
2023-01-16 09:53:52 +00:00
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<Selectable<'c>>, lower: &Arc<Selectable<'c>>) {
2024-05-17 13:12:48 +00:00
if upper.isolate || lower.isolate {
return;
}
2023-01-16 09:53:52 +00:00
*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<Selectable<'c>>, right: &Arc<Selectable<'c>>) {
2024-05-17 13:12:48 +00:00
if left.isolate || right.isolate {
return;
}
2023-01-16 09:53:52 +00:00
*left.east_neighbour.write().unwrap() = Some(Arc::downgrade(right));
*right.west_neighbour.write().unwrap() = Some(Arc::downgrade(left));
}
}