276 lines
8.3 KiB
Rust
276 lines
8.3 KiB
Rust
|
//! `Iconizable` is a property to display a icon on an item
|
||
|
|
||
|
use crate::prelude::*;
|
||
|
use anyhow::Result;
|
||
|
use assetpath::AssetPath;
|
||
|
|
||
|
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())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// `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,
|
||
|
}
|
||
|
|
||
|
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>,
|
||
|
) -> 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),
|
||
|
});
|
||
|
|
||
|
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
|
||
|
let parent_left = self.framable.left();
|
||
|
let parent_right = self.framable.right();
|
||
|
let parent_top = self.framable.top();
|
||
|
let parent_bottom = self.framable.bottom();
|
||
|
|
||
|
let parent_width = parent_right as i32 - parent_left;
|
||
|
let parent_height = parent_bottom as i32 - parent_top;
|
||
|
|
||
|
// 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()
|
||
|
}
|
||
|
}
|