2023-01-16 09:53:52 +00:00
|
|
|
//! `Textable` is a property to display text on an item
|
|
|
|
|
|
|
|
use super::writeable::ModifyText;
|
|
|
|
use crate::prelude::*;
|
|
|
|
use anyhow::Result;
|
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::{
|
|
|
|
convert::TryFrom,
|
|
|
|
sync::{
|
|
|
|
Arc, RwLock,
|
2025-03-04 08:11:05 +00:00
|
|
|
atomic::{AtomicI32, AtomicU32, Ordering::SeqCst},
|
2023-01-16 09:53:52 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
/// `TextAlignment` is used to describe where the text of `Textable` is aligned to
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
|
|
pub enum TextAlignment {
|
|
|
|
Left,
|
|
|
|
Right,
|
|
|
|
Top,
|
|
|
|
Bottom,
|
|
|
|
Center,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for TextAlignment {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::Center
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<String> for TextAlignment {
|
|
|
|
type Error = anyhow::Error;
|
|
|
|
|
|
|
|
fn try_from(text: String) -> Result<TextAlignment> {
|
|
|
|
Ok(match text.as_str() {
|
|
|
|
"left" => Self::Left,
|
|
|
|
"right" => Self::Right,
|
|
|
|
"top" => Self::Top,
|
|
|
|
"bottom" => Self::Bottom,
|
|
|
|
"center" => Self::Center,
|
|
|
|
|
|
|
|
_ => {
|
|
|
|
return Err(anyhow::Error::msg(format!(
|
|
|
|
"Failed parsing TextAlignment from: {}",
|
|
|
|
text
|
2025-03-04 08:11:05 +00:00
|
|
|
)));
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `Textable` gives the ability to display text inside an item
|
|
|
|
pub struct Textable {
|
|
|
|
framable: Arc<Framable>,
|
|
|
|
|
|
|
|
text_alignment: RwLock<TextAlignment>,
|
|
|
|
text: RwLock<String>,
|
|
|
|
vertex_count: AtomicU32,
|
|
|
|
height_ratio: RwLock<f32>,
|
|
|
|
character_size: AtomicU32,
|
|
|
|
|
|
|
|
descriptor_set: RwLock<Arc<DescriptorSet>>,
|
|
|
|
|
|
|
|
buffer: Arc<RwLock<Option<Arc<Buffer<TexturedVertex>>>>>,
|
|
|
|
|
|
|
|
ui_layer: AtomicI32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Textable {
|
|
|
|
/// Factory method for `Textable`, returns `Arc<Textable>`.
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `framable` is a `Arc<Framable>` instance
|
|
|
|
/// * `text` the text to be displayed
|
|
|
|
/// * `height_ratio` the ratio of the height in respect to the framable height
|
|
|
|
/// * `text_alignment` where the text is aligned to
|
|
|
|
pub fn new(
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2023-01-16 09:53:52 +00:00
|
|
|
framable: Arc<Framable>,
|
|
|
|
text: String,
|
|
|
|
height_ratio: f32,
|
|
|
|
text_alignment: TextAlignment,
|
|
|
|
text_color: Color,
|
|
|
|
) -> Result<Arc<Self>> {
|
2025-03-04 08:11:05 +00:00
|
|
|
let set = gui_handler.color_descriptor(text_color)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let buffer = if text.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(Self::create_text_buffer(
|
2025-03-04 08:11:05 +00:00
|
|
|
gui_handler.device(),
|
2023-01-16 09:53:52 +00:00
|
|
|
text.len() as u32 * 6,
|
|
|
|
)?)
|
|
|
|
};
|
|
|
|
|
|
|
|
let textable = Arc::new(Textable {
|
|
|
|
vertex_count: AtomicU32::new(text.len() as u32 * 6),
|
|
|
|
text_alignment: RwLock::new(text_alignment),
|
|
|
|
text: RwLock::new(text),
|
|
|
|
height_ratio: RwLock::new(height_ratio),
|
|
|
|
character_size: AtomicU32::new(
|
|
|
|
((framable.bottom() as i32 - framable.top()) as f32 * height_ratio) as u32,
|
|
|
|
),
|
|
|
|
|
|
|
|
framable,
|
|
|
|
|
|
|
|
descriptor_set: RwLock::new(set),
|
|
|
|
|
|
|
|
buffer: Arc::new(RwLock::new(buffer)),
|
|
|
|
|
|
|
|
ui_layer: AtomicI32::new(0),
|
|
|
|
});
|
|
|
|
|
|
|
|
let textable_clone = textable.clone();
|
|
|
|
let weak_textable = Arc::downgrade(&textable);
|
|
|
|
|
|
|
|
textable.framable.add_callback(
|
|
|
|
weak_textable,
|
2025-03-04 08:11:05 +00:00
|
|
|
Box::new(move |gui_handler| textable_clone.update_text(gui_handler)),
|
2023-01-16 09:53:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
Ok(textable)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_text_buffer(
|
|
|
|
device: &Arc<Device>,
|
|
|
|
letter_count: u32,
|
|
|
|
) -> Result<Arc<Buffer<TexturedVertex>>> {
|
|
|
|
Buffer::builder()
|
|
|
|
.set_usage(VK_BUFFER_USAGE_VERTEX_BUFFER_BIT)
|
|
|
|
.set_memory_usage(MemoryUsage::CpuOnly)
|
|
|
|
.set_size(letter_count as VkDeviceSize)
|
|
|
|
.build(device.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Add method
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `textable` is a `&Arc<Textable>` instance that is going to be added
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn add(self: &Arc<Self>, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 08:11:05 +00:00
|
|
|
gui_handler.add_textable(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
|
|
|
|
///
|
|
|
|
/// * `textable` is a `&Arc<Textable>` instance that is going to be deleted
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn delete(self: &Arc<Self>, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 08:11:05 +00:00
|
|
|
gui_handler.delete_textable(self.ui_layer.load(SeqCst), self)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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_textable = Arc::downgrade(self);
|
|
|
|
self.framable.remove_callback(weak_textable);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Changes text color
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `text_color` defines the color of the text
|
2025-03-04 08:11:05 +00:00
|
|
|
pub fn set_text_color(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 08:11:05 +00:00
|
|
|
text_color: Color,
|
|
|
|
) -> Result<()> {
|
|
|
|
let set = gui_handler.color_descriptor(text_color)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
*self.descriptor_set.write().unwrap() = set;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the text alignment
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `text_alignment` where the text is aligned to
|
2025-03-04 08:11:05 +00:00
|
|
|
pub fn set_text_alignment(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 08:11:05 +00:00
|
|
|
text_alignment: TextAlignment,
|
|
|
|
) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
*self.text_alignment.write().unwrap() = text_alignment;
|
2025-03-04 08:11:05 +00:00
|
|
|
self.update_text(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the count of vertices described by the buffer
|
|
|
|
pub fn vertex_count(&self) -> u32 {
|
|
|
|
self.vertex_count.load(SeqCst)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the internal vulkan buffer
|
|
|
|
pub fn buffer(&self) -> Option<Arc<Buffer<TexturedVertex>>> {
|
|
|
|
self.buffer.read().unwrap().clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the internal vulkan descriptor set
|
|
|
|
pub fn descriptor_set(&self) -> Arc<DescriptorSet> {
|
|
|
|
self.descriptor_set.read().unwrap().clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the text with height ratio
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `text` the text to be displayed
|
|
|
|
/// * `height_ratio` the ratio of the height in respect to the framable height
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn set_text(&self, gui_handler: &mut GuiHandler, text: impl ToString) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let text = text.to_string();
|
|
|
|
|
|
|
|
if text.is_empty() {
|
|
|
|
self.vertex_count.store(0, SeqCst);
|
|
|
|
*self.text.write().unwrap() = String::new();
|
|
|
|
|
2025-03-04 08:11:05 +00:00
|
|
|
gui_handler.enqueue_text_update({
|
2023-01-16 09:53:52 +00:00
|
|
|
let weak_buffer = Arc::downgrade(&self.buffer);
|
|
|
|
|
|
|
|
Box::new(move || {
|
|
|
|
if let Some(buffer) = weak_buffer.upgrade() {
|
|
|
|
*buffer.write().unwrap() = None;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
})
|
|
|
|
});
|
|
|
|
} else {
|
|
|
|
self.vertex_count.store(text.len() as u32 * 6, SeqCst);
|
|
|
|
*self.text.write().unwrap() = text;
|
2025-03-04 08:11:05 +00:00
|
|
|
self.update_text(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the text size with the given height ratio
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
///
|
|
|
|
/// * `height_ratio` the ratio of the height in respect to the framable height
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn set_size(&self, gui_handler: &mut GuiHandler, height_ratio: f32) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
*self.height_ratio.write().unwrap() = height_ratio;
|
2025-03-04 08:11:05 +00:00
|
|
|
self.update_text(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the text
|
|
|
|
pub fn text(&self) -> String {
|
|
|
|
self.text.read().unwrap().clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calculate_text_size(&self) {
|
|
|
|
self.character_size.store(
|
|
|
|
((self.framable.bottom() as i32 - self.framable.top()) as f32
|
|
|
|
* *self.height_ratio.read().unwrap()) as u32,
|
|
|
|
SeqCst,
|
|
|
|
);
|
|
|
|
|
|
|
|
let width = self.framable.right() as i32 - self.framable.left();
|
|
|
|
let text_len = self.text.read().unwrap().len();
|
|
|
|
|
|
|
|
let text_width = (self.character_size.load(SeqCst) as f32 * 0.5 * text_len as f32) as u32;
|
|
|
|
|
|
|
|
if text_width > width as u32 {
|
|
|
|
self.character_size.store(
|
|
|
|
((self.framable.right() as i32 - self.framable.left()) / text_len as i32) as u32
|
|
|
|
* 2,
|
|
|
|
SeqCst,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Updates the texts buffer
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn update_text(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
self.calculate_text_size();
|
|
|
|
|
|
|
|
let (x, y) = self.calc_pos_from_alignment();
|
|
|
|
|
2025-03-04 08:11:05 +00:00
|
|
|
self.create_buffer(gui_handler, x, y)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn center_y(&self) -> f32 {
|
|
|
|
(self.framable.top() + self.framable.bottom()) as f32 * 0.5
|
|
|
|
- self.character_size.load(SeqCst) as f32 * 0.5
|
|
|
|
}
|
|
|
|
|
|
|
|
fn center_x(&self) -> f32 {
|
|
|
|
(self.framable.left() + self.framable.right()) as f32 * 0.5
|
|
|
|
- (self.character_size.load(SeqCst) as f32
|
|
|
|
* 0.25
|
|
|
|
* self.text.read().unwrap().len() as f32)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn calc_pos_from_alignment(&self) -> (f32, f32) {
|
|
|
|
match *self.text_alignment.read().unwrap() {
|
|
|
|
TextAlignment::Left => (self.framable.left() as f32, self.center_y()),
|
|
|
|
TextAlignment::Right => (
|
|
|
|
self.framable.right() as f32
|
|
|
|
- (self.character_size.load(SeqCst) as f32
|
|
|
|
* 0.5
|
|
|
|
* self.text.read().unwrap().len() as f32),
|
|
|
|
self.center_y(),
|
|
|
|
),
|
|
|
|
TextAlignment::Top => (self.center_x(), self.framable.top() as f32),
|
|
|
|
TextAlignment::Bottom => (
|
|
|
|
self.center_x(),
|
|
|
|
(self.framable.bottom() - self.character_size.load(SeqCst) as i32) as f32,
|
|
|
|
),
|
|
|
|
TextAlignment::Center => (self.center_x(), self.center_y()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 08:11:05 +00:00
|
|
|
fn create_buffer(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 08:11:05 +00:00
|
|
|
win_x: f32,
|
|
|
|
win_y: f32,
|
|
|
|
) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let weak_buffer = Arc::downgrade(&self.buffer);
|
|
|
|
let text = self.text.read().unwrap().clone();
|
|
|
|
let character_size = self.character_size.load(SeqCst);
|
2025-03-04 08:11:05 +00:00
|
|
|
let ortho = gui_handler.ortho();
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 08:11:05 +00:00
|
|
|
let device = gui_handler.device().clone();
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let async_buffer_creation = Box::new(move || {
|
|
|
|
if let Some(buffer) = weak_buffer.upgrade() {
|
|
|
|
let mut offset = 0.0;
|
|
|
|
|
|
|
|
// variable to calculate letter position in bitmap font
|
|
|
|
let letters_in_row = 16;
|
|
|
|
let inverse_row = 0.0625;
|
|
|
|
let inverse_col = 0.125;
|
|
|
|
|
|
|
|
let letter_height = character_size as f32;
|
|
|
|
let letter_width = letter_height * 0.5;
|
|
|
|
|
|
|
|
let priority = 0.0;
|
|
|
|
|
|
|
|
let mut buffer = buffer.write().unwrap();
|
|
|
|
|
|
|
|
let text_buffer_len = text.len() as u32 * 6;
|
|
|
|
|
|
|
|
match buffer.as_mut() {
|
|
|
|
Some(buf) => {
|
|
|
|
if buf.size() != text_buffer_len as VkDeviceSize {
|
|
|
|
*buffer = if text.is_empty() {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(Self::create_text_buffer(&device, text_buffer_len)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
if !text.is_empty() {
|
|
|
|
*buffer = Some(Self::create_text_buffer(&device, text_buffer_len)?)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(buffer) = buffer.as_ref() {
|
|
|
|
let mut buffer_mapping = buffer.map_complete()?;
|
|
|
|
|
|
|
|
let mut i = 0;
|
|
|
|
|
|
|
|
for letter in text.chars() {
|
|
|
|
let mod_number = if letter as u32 >= 32 {
|
|
|
|
letter as u32 - 32
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
// coordinates to describe letter position in bitmap font
|
|
|
|
let y = ((mod_number as f32) * inverse_row).floor();
|
|
|
|
let x = (mod_number - (y as u32 * letters_in_row)) as f32;
|
|
|
|
|
|
|
|
buffer_mapping[i] = TexturedVertex {
|
2024-05-13 13:03:54 +00:00
|
|
|
position: (ortho
|
2023-01-16 09:53:52 +00:00
|
|
|
* cgmath::Vector4::new(
|
|
|
|
win_x + offset,
|
|
|
|
win_y + letter_height,
|
|
|
|
priority,
|
|
|
|
1.0,
|
2024-05-13 13:03:54 +00:00
|
|
|
))
|
|
|
|
.xy(),
|
2023-01-16 09:53:52 +00:00
|
|
|
texture_coordinates: cgmath::Vector2::new(
|
|
|
|
inverse_row * x,
|
|
|
|
inverse_col + inverse_col * y,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
buffer_mapping[i] = TexturedVertex {
|
2024-05-13 13:03:54 +00:00
|
|
|
position: (ortho
|
2023-01-16 09:53:52 +00:00
|
|
|
* cgmath::Vector4::new(
|
|
|
|
win_x + offset + letter_width,
|
|
|
|
win_y + letter_height,
|
|
|
|
priority,
|
|
|
|
1.0,
|
2024-05-13 13:03:54 +00:00
|
|
|
))
|
|
|
|
.xy(),
|
2023-01-16 09:53:52 +00:00
|
|
|
texture_coordinates: cgmath::Vector2::new(
|
|
|
|
inverse_row + inverse_row * x,
|
|
|
|
inverse_col + inverse_col * y,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
buffer_mapping[i] = TexturedVertex {
|
2024-05-13 13:03:54 +00:00
|
|
|
position: (ortho
|
2023-01-16 09:53:52 +00:00
|
|
|
* cgmath::Vector4::new(
|
|
|
|
win_x + offset + letter_width,
|
|
|
|
win_y,
|
|
|
|
priority,
|
|
|
|
1.0,
|
2024-05-13 13:03:54 +00:00
|
|
|
))
|
|
|
|
.xy(),
|
2023-01-16 09:53:52 +00:00
|
|
|
texture_coordinates: cgmath::Vector2::new(
|
|
|
|
inverse_row + inverse_row * x,
|
|
|
|
inverse_col * y,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
buffer_mapping[i] = TexturedVertex {
|
2024-05-13 13:03:54 +00:00
|
|
|
position: (ortho
|
2023-01-16 09:53:52 +00:00
|
|
|
* cgmath::Vector4::new(
|
|
|
|
win_x + offset + letter_width,
|
|
|
|
win_y,
|
|
|
|
priority,
|
|
|
|
1.0,
|
2024-05-13 13:03:54 +00:00
|
|
|
))
|
|
|
|
.xy(),
|
2023-01-16 09:53:52 +00:00
|
|
|
texture_coordinates: cgmath::Vector2::new(
|
|
|
|
inverse_row + inverse_row * x,
|
|
|
|
inverse_col * y,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
buffer_mapping[i] = TexturedVertex {
|
2024-05-13 13:03:54 +00:00
|
|
|
position: (ortho
|
|
|
|
* cgmath::Vector4::new(win_x + offset, win_y, priority, 1.0))
|
|
|
|
.xy(),
|
2023-01-16 09:53:52 +00:00
|
|
|
texture_coordinates: cgmath::Vector2::new(
|
|
|
|
inverse_row * x,
|
|
|
|
inverse_col * y,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
buffer_mapping[i] = TexturedVertex {
|
2024-05-13 13:03:54 +00:00
|
|
|
position: (ortho
|
2023-01-16 09:53:52 +00:00
|
|
|
* cgmath::Vector4::new(
|
|
|
|
win_x + offset,
|
|
|
|
win_y + letter_height,
|
|
|
|
priority,
|
|
|
|
1.0,
|
2024-05-13 13:03:54 +00:00
|
|
|
))
|
|
|
|
.xy(),
|
2023-01-16 09:53:52 +00:00
|
|
|
texture_coordinates: cgmath::Vector2::new(
|
|
|
|
inverse_row * x,
|
|
|
|
inverse_col + inverse_col * y,
|
|
|
|
),
|
|
|
|
};
|
|
|
|
|
|
|
|
i += 1;
|
|
|
|
|
|
|
|
offset += letter_width;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
|
2025-03-04 08:11:05 +00:00
|
|
|
gui_handler.enqueue_text_update(async_buffer_creation);
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn text_changed_through_write(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
self.vertex_count
|
|
|
|
.store(self.text.read().unwrap().len() as u32 * 6, SeqCst);
|
|
|
|
self.character_size.store(
|
|
|
|
((self.framable.bottom() as i32 - self.framable.top()) as f32
|
|
|
|
* *self.height_ratio.read().unwrap()) as u32,
|
|
|
|
SeqCst,
|
|
|
|
);
|
|
|
|
|
2025-03-04 08:11:05 +00:00
|
|
|
self.update_text(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ModifyText for Textable {
|
2025-03-04 11:25:02 +00:00
|
|
|
fn set_text(&self, gui_handler: &mut GuiHandler, text: String) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
*self.text.write().unwrap() = text;
|
2025-03-04 08:11:05 +00:00
|
|
|
self.text_changed_through_write(gui_handler)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn add_letter(&self, gui_handler: &mut GuiHandler, letter: char) -> Result<String> {
|
2023-01-16 09:53:52 +00:00
|
|
|
self.text.write().unwrap().push(letter);
|
2025-03-04 08:11:05 +00:00
|
|
|
self.text_changed_through_write(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(self.text.read().unwrap().clone())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn remove_last(&self, gui_handler: &mut GuiHandler) -> Result<Option<String>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
self.text.write().unwrap().pop();
|
2025-03-04 08:11:05 +00:00
|
|
|
self.text_changed_through_write(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let text = self.text.read().unwrap().clone();
|
|
|
|
|
|
|
|
if text.is_empty() {
|
|
|
|
Ok(None)
|
|
|
|
} else {
|
|
|
|
Ok(Some(text))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|