ui/src/guihandler/gui/iconizable.rs

298 lines
9.1 KiB
Rust
Raw Normal View History

2023-01-16 09:53:52 +00:00
//! `Iconizable` is a property to display a icon on an item
use crate::prelude::*;
use anyhow::Result;
use assetpath::AssetPath;
2023-01-16 11:58:59 +00:00
use utilities::prelude::*;
use vulkan_rs::prelude::*;
2023-01-16 09:53:52 +00:00
use super::texturedvertex::TexturedVertex;
use std::sync::{
atomic::{AtomicI32, AtomicU32, Ordering::SeqCst},
Arc, RwLock,
};
#[derive(Clone, Debug)]
pub enum IconBuilderType {
Path(AssetPath),
Image(Arc<Image>),
}
impl From<String> for IconBuilderType {
fn from(s: String) -> Self {
IconBuilderType::Path(AssetPath::from(s))
}
}
impl From<&String> for IconBuilderType {
fn from(s: &String) -> Self {
IconBuilderType::Path(AssetPath::from(s.as_str()))
}
}
impl From<&str> for IconBuilderType {
fn from(s: &str) -> Self {
IconBuilderType::Path(AssetPath::from(s))
}
}
impl From<AssetPath> for IconBuilderType {
fn from(assetpath: AssetPath) -> Self {
IconBuilderType::Path(assetpath)
}
}
impl From<Arc<Image>> for IconBuilderType {
fn from(image: Arc<Image>) -> Self {
IconBuilderType::Image(image)
}
}
impl From<&Arc<Image>> for IconBuilderType {
fn from(image: &Arc<Image>) -> Self {
IconBuilderType::Image(image.clone())
}
}
2024-04-04 15:57:50 +00:00
#[derive(Debug, Clone)]
pub struct IconizablePositioning {
pub left: f32,
pub right: f32,
pub top: f32,
pub bottom: f32,
}
2023-01-16 09:53:52 +00:00
/// `Iconizable` gives the ability to display a icon as icon on an UI item
pub struct Iconizable {
framable: Arc<Framable>,
buffer: Arc<Buffer<TexturedVertex>>,
icon: RwLock<Arc<Image>>,
descriptor_set: Arc<DescriptorSet>,
margin: AtomicU32,
ui_layer: AtomicI32,
2024-04-04 15:57:50 +00:00
positioning: Option<IconizablePositioning>,
2023-01-16 09:53:52 +00:00
}
impl Iconizable {
/// Factory method for `Iconizable`, returns `Arc<Iconizable>`
///
/// # Arguments
///
/// * `framable` is a `Arc<Framable>` instance
/// * `icon` is a reference to an `Arc<Image>`
pub fn new(
framable: Arc<Framable>,
icon_builder: impl Into<IconBuilderType>,
2024-04-04 15:57:50 +00:00
positioning: impl Into<Option<IconizablePositioning>>,
2023-01-16 09:53:52 +00:00
) -> Result<Arc<Self>> {
let icon = framable.gui_handler().request_icon(icon_builder)?;
let device = framable.gui_handler().device();
let desc_pool = DescriptorPool::builder()
.set_layout(framable.gui_handler().icon_descriptor_layout().clone())
.build(device.clone())?;
let descriptor_set = DescriptorPool::prepare_set(&desc_pool).allocate()?;
descriptor_set.update(&[DescriptorWrite::combined_samplers(0, &[&icon])])?;
let buffer = Buffer::builder()
.set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
.set_memory_usage(MemoryUsage::CpuOnly)
.set_size(6)
.build(device.clone())?;
let iconizable = Arc::new(Iconizable {
framable,
buffer,
icon: RwLock::new(icon),
descriptor_set,
margin: AtomicU32::new(0),
ui_layer: AtomicI32::new(0),
2024-04-04 15:57:50 +00:00
positioning: positioning.into(),
2023-01-16 09:53:52 +00:00
});
let iconizable_clone = iconizable.clone();
let weak_iconizable = Arc::downgrade(&iconizable);
iconizable.framable.add_callback(
weak_iconizable,
Box::new(move || iconizable_clone.update_frame()),
);
Ok(iconizable)
}
/// Add method
///
/// # Arguments
///
/// * `iconizable` is a `&Arc<Iconizable>` instance that is going to be added
pub fn add(self: &Arc<Self>) -> Result<()> {
self.framable
.gui_handler()
.add_iconizable(self.ui_layer.load(SeqCst), self.clone())
}
/// Delete method, has to be explicitly called, otherwise it will remain in memory
///
/// # Arguments
///
/// * `iconizable` is a `&Arc<Iconizable>` instance that is going to be deleted
pub fn delete(self: &Arc<Self>) -> Result<()> {
self.framable
.gui_handler()
.delete_iconizable(self.ui_layer.load(SeqCst), self)
}
pub fn set_ui_layer(&self, ui_layer: i32) {
self.ui_layer.store(ui_layer, SeqCst);
}
pub fn clear_callback(self: &Arc<Self>) {
let weak_iconizable = Arc::downgrade(self);
self.framable.remove_callback(weak_iconizable);
}
/// Sets the frame parameter for
///
/// # Arguments
///
/// * `vertical_alignment` where this icon is aligned to inside the framable
/// * `horizontal_alignment` where this icon is aligned to inside the framable
pub fn set_margin(&self, margin: u32) -> Result<()> {
self.margin.store(margin, SeqCst);
Ok(())
}
/// Updates the frame
pub fn update_frame(&self) -> Result<()> {
assert!(self.framable.is_framed(), "framable is not framed yet");
// frame parameter
2024-04-04 15:57:50 +00:00
let mut parent_left = self.framable.left();
2023-01-16 09:53:52 +00:00
let parent_right = self.framable.right();
2024-04-04 15:57:50 +00:00
let mut parent_top = self.framable.top();
2023-01-16 09:53:52 +00:00
let parent_bottom = self.framable.bottom();
2024-04-04 15:57:50 +00:00
let mut parent_width = parent_right as i32 - parent_left;
let mut parent_height = parent_bottom as i32 - parent_top;
if let Some(positioning) = &self.positioning {
parent_left += ((parent_width as f32 / 2.0) * positioning.left) as i32;
parent_width =
(parent_width as f32 * ((positioning.left + positioning.right) / 2.0)) as i32;
parent_top += ((parent_height as f32 / 2.0) * positioning.top) as i32;
parent_height =
(parent_height as f32 * ((positioning.top + positioning.bottom) / 2.0)) as i32;
}
2023-01-16 09:53:52 +00:00
// icon parameter
let (icon_width, icon_height) = {
let icon = self.icon.read().unwrap();
let width = icon.width();
let height = icon.height();
(width, height)
};
// check how the icon fits best into the frame
let margin = self.margin.load(SeqCst);
let actual_icon_width = icon_width + 2 * margin;
let actual_icon_height = icon_height + 2 * margin;
let icon_ratio = actual_icon_width as f32 / actual_icon_height as f32;
let parent_ratio = parent_width as f32 / parent_height as f32;
let (resulting_width, resulting_height) = if parent_ratio > icon_ratio {
// this case means, that the parent is wider than the icon
let resulting_height = parent_height as f32;
let resulting_width = resulting_height * icon_ratio;
(resulting_width, resulting_height)
} else if parent_ratio < icon_ratio {
let resulting_width = parent_width as f32;
let resulting_height = resulting_width / icon_ratio;
(resulting_width, resulting_height)
} else {
(parent_width as f32, parent_height as f32)
};
let mut top = (parent_height as f32 / 2.0) - (resulting_height / 2.0);
let mut left = (parent_width as f32 / 2.0) - (resulting_width / 2.0);
left += parent_left as f32;
top += parent_top as f32;
let right = left + resulting_width;
let bottom = top + resulting_height;
let ortho = self.framable.ortho();
let mut frame = self.buffer.map_complete()?;
let abs_left = left + margin as f32;
let abs_right = right - margin as f32;
let abs_top = top + margin as f32;
let abs_bottom = bottom - margin as f32;
frame[0].position = ortho * cgmath::Vector4::new(abs_left, abs_bottom, 0.0, 1.0);
frame[1].position = ortho * cgmath::Vector4::new(abs_right, abs_bottom, 0.0, 1.0);
frame[2].position = ortho * cgmath::Vector4::new(abs_right, abs_top, 0.0, 1.0);
frame[3].position = ortho * cgmath::Vector4::new(abs_right, abs_top, 0.0, 1.0);
frame[4].position = ortho * cgmath::Vector4::new(abs_left, abs_top, 0.0, 1.0);
frame[5].position = ortho * cgmath::Vector4::new(abs_left, abs_bottom, 0.0, 1.0);
frame[0].texture_coordinates = cgmath::Vector2::new(0.0, 1.0);
frame[1].texture_coordinates = cgmath::Vector2::new(1.0, 1.0);
frame[2].texture_coordinates = cgmath::Vector2::new(1.0, 0.0);
frame[3].texture_coordinates = cgmath::Vector2::new(1.0, 0.0);
frame[4].texture_coordinates = cgmath::Vector2::new(0.0, 0.0);
frame[5].texture_coordinates = cgmath::Vector2::new(0.0, 1.0);
Ok(())
}
/// Replace the current icon with a new one
///
/// # Arguments
///
/// * `icon` the new icon
pub fn set_icon(&self, icon_builder: impl Into<IconBuilderType>) -> Result<()> {
let icon = self.framable.gui_handler().request_icon(icon_builder)?;
self.descriptor_set
.update(&[DescriptorWrite::combined_samplers(0, &[&icon])])?;
*self.icon.write().unwrap() = icon;
Ok(())
}
/// Returns the internal vulkan buffer
pub fn buffer(&self) -> &Arc<Buffer<TexturedVertex>> {
&self.buffer
}
/// Returns the internal descriptor set
pub fn descriptor_set(&self) -> &Arc<DescriptorSet> {
&self.descriptor_set
}
pub fn icon(&self) -> Arc<Image> {
self.icon.read().unwrap().clone()
}
}