2024-04-04 15:57:50 +00:00
|
|
|
use crate::{
|
2025-02-27 09:43:07 +00:00
|
|
|
builder::validator::buttoninfo::ButtonInfo,
|
|
|
|
gui_handler::gui::iconizable::IconizablePositioning, prelude::*,
|
2024-04-04 15:57:50 +00:00
|
|
|
};
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-02-28 05:26:57 +00:00
|
|
|
use anyhow::{Result, anyhow};
|
2023-01-16 09:53:52 +00:00
|
|
|
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::{
|
2025-02-28 05:26:57 +00:00
|
|
|
IconBuilderType,
|
2023-01-16 09:53:52 +00:00
|
|
|
fill_type::{FillType, InnerFillType},
|
2024-04-04 15:57:50 +00:00
|
|
|
wrapper::{IconizableWrapper, TextableWrapper},
|
2023-01-16 09:53:52 +00:00
|
|
|
};
|
|
|
|
use cgmath::{vec2, vec4};
|
|
|
|
|
2024-05-25 07:42:05 +00:00
|
|
|
use std::{
|
|
|
|
ops::Deref,
|
|
|
|
sync::{
|
|
|
|
Arc, Mutex,
|
2025-02-28 05:26:57 +00:00
|
|
|
atomic::{AtomicBool, Ordering::SeqCst},
|
2024-05-25 07:42:05 +00:00
|
|
|
},
|
2023-01-16 09:53:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
pub struct ButtonBuilder {
|
|
|
|
icon: Option<Arc<Image>>,
|
|
|
|
margin: u32,
|
|
|
|
|
2024-05-17 13:12:48 +00:00
|
|
|
isolate: bool,
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
text: String,
|
|
|
|
text_color: Color,
|
|
|
|
ratio: f32,
|
|
|
|
text_alignment: TextAlignment,
|
|
|
|
|
|
|
|
button_select_mode: ButtonSelectMode,
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
click_sound: Option<AssetPath>,
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
hover_sound: Option<AssetPath>,
|
|
|
|
|
|
|
|
normal: Option<FillTypeInfo>,
|
|
|
|
selected: Option<FillTypeInfo>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl ButtonBuilder {
|
2024-05-17 13:12:48 +00:00
|
|
|
pub fn set_isolate(mut self, isolate: bool) -> Self {
|
|
|
|
self.isolate = isolate;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn set_select_mode(mut self, select_mode: ButtonSelectMode) -> Self {
|
|
|
|
self.button_select_mode = select_mode;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_normal(mut self, normal: impl Into<FillTypeInfo>) -> Self {
|
|
|
|
self.normal = Some(normal.into());
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_selected(mut self, selected: impl Into<FillTypeInfo>) -> Self {
|
|
|
|
self.selected = Some(selected.into());
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_icon(mut self, icon: Option<Arc<Image>>, margin: u32) -> Self {
|
|
|
|
self.icon = icon;
|
|
|
|
self.margin = margin;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_ratio(mut self, ratio: f32) -> Self {
|
|
|
|
self.ratio = ratio;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_alignment(mut self, text_alignment: TextAlignment) -> Self {
|
|
|
|
self.text_alignment = text_alignment;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_color(mut self, text_color: Color) -> Self {
|
|
|
|
self.text_color = text_color;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text(mut self, text: impl Into<String>) -> Self {
|
|
|
|
self.text = text.into();
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
pub fn set_click_sound(mut self, path: AssetPath) -> Self {
|
|
|
|
self.click_sound = Some(path);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
pub fn set_hover_sound(mut self, path: AssetPath) -> Self {
|
|
|
|
self.hover_sound = Some(path);
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn build<'a>(self, gui_handler: &mut GuiHandler<'_>) -> Result<Arc<Button<'a>>> {
|
|
|
|
let framable = Framable::new(gui_handler.width(), gui_handler.height(), false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let normal = FillType::new(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2023-01-16 09:53:52 +00:00
|
|
|
framable.clone(),
|
2025-02-28 05:26:57 +00:00
|
|
|
self.normal
|
|
|
|
.ok_or(anyhow!("normal button layout not set!"))?,
|
2023-01-16 09:53:52 +00:00
|
|
|
)?;
|
|
|
|
|
|
|
|
let selected = FillType::new(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2023-01-16 09:53:52 +00:00
|
|
|
framable.clone(),
|
2025-02-28 05:26:57 +00:00
|
|
|
self.selected
|
|
|
|
.ok_or(anyhow!("selected button layout not set!"))?,
|
2023-01-16 09:53:52 +00:00
|
|
|
)?;
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let click_executable = Executable::new();
|
|
|
|
let select_executable = Executable::new();
|
|
|
|
let on_select_executable = Executable::new();
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
let click_sound = self
|
|
|
|
.click_sound
|
|
|
|
.map(|path| Audible::new(gui_handler.clone(), path))
|
|
|
|
.transpose()?;
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
let hover_sound = self
|
|
|
|
.hover_sound
|
|
|
|
.map(|path| Audible::new(gui_handler.clone(), path))
|
|
|
|
.transpose()?;
|
|
|
|
|
|
|
|
let clickable = Clickable::new(framable.clone(), click_executable.clone());
|
|
|
|
|
|
|
|
let selectable = Selectable::new(
|
|
|
|
click_executable.clone(),
|
|
|
|
select_executable.clone(),
|
|
|
|
on_select_executable.clone(),
|
2024-05-17 13:12:48 +00:00
|
|
|
self.isolate,
|
2023-01-16 09:53:52 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let hoverable = Hoverable::new(
|
|
|
|
framable.clone(),
|
|
|
|
select_executable.clone(),
|
|
|
|
on_select_executable.clone(),
|
|
|
|
);
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
if let Some(click_sound) = &click_sound {
|
|
|
|
clickable.set_audible(Some(click_sound.clone()));
|
|
|
|
selectable.set_click_audible(Some(click_sound.clone()));
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
if let Some(hover_sound) = &hover_sound {
|
|
|
|
selectable.set_select_audible(Some(hover_sound.clone()));
|
|
|
|
hoverable.set_audible(Some(hover_sound.clone()));
|
|
|
|
}
|
|
|
|
|
|
|
|
let textable = TextableWrapper::new(
|
|
|
|
framable.clone(),
|
|
|
|
self.text_color,
|
|
|
|
self.ratio,
|
|
|
|
self.text_alignment,
|
|
|
|
);
|
|
|
|
|
|
|
|
if !self.text.is_empty() {
|
2025-03-04 10:37:45 +00:00
|
|
|
textable.set_text(gui_handler, &self.text, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let iconizable = IconizableWrapper::new(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2023-01-16 09:53:52 +00:00
|
|
|
framable.clone(),
|
|
|
|
self.icon.map(IconBuilderType::Image),
|
2024-04-04 15:57:50 +00:00
|
|
|
None,
|
|
|
|
self.margin,
|
|
|
|
)?;
|
|
|
|
|
2024-04-26 06:28:05 +00:00
|
|
|
let info_icon = IconizableWrapper::new(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2024-04-04 15:57:50 +00:00
|
|
|
framable.clone(),
|
|
|
|
None,
|
|
|
|
Some(IconizablePositioning {
|
|
|
|
left: 1.2,
|
|
|
|
right: 0.0,
|
|
|
|
top: 1.2,
|
|
|
|
bottom: 0.0,
|
|
|
|
}),
|
2023-01-16 09:53:52 +00:00
|
|
|
self.margin,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
let button = Arc::new(Button {
|
|
|
|
framable,
|
|
|
|
clickable,
|
|
|
|
hoverable,
|
|
|
|
selectable,
|
|
|
|
textable,
|
|
|
|
iconizable,
|
2024-04-26 06:28:05 +00:00
|
|
|
info_icon,
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
_click_sound: click_sound,
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
_hover_sound: hover_sound,
|
|
|
|
|
|
|
|
click_executable,
|
|
|
|
select_executable,
|
|
|
|
on_select_executable,
|
|
|
|
|
|
|
|
normal,
|
|
|
|
selected,
|
|
|
|
|
|
|
|
button_state: Mutex::new(ButtonState::Normal),
|
|
|
|
|
|
|
|
visible: AtomicBool::new(false),
|
|
|
|
|
|
|
|
select_mode: self.button_select_mode,
|
|
|
|
});
|
|
|
|
|
|
|
|
// Button::create_hovered_changed_callback(button.clone())?;
|
|
|
|
Button::create_clicked_changed_callback(button.clone());
|
|
|
|
Button::create_selected_changed_callback(button.clone());
|
|
|
|
|
|
|
|
Ok(button)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
enum ButtonState {
|
|
|
|
Normal,
|
|
|
|
Selected,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
|
|
pub enum ButtonSelectMode {
|
|
|
|
None,
|
|
|
|
Bigger,
|
|
|
|
}
|
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
pub struct Button<'a> {
|
2023-01-16 09:53:52 +00:00
|
|
|
clickable: Arc<Clickable>,
|
|
|
|
hoverable: Arc<Hoverable>,
|
2025-03-03 19:01:17 +00:00
|
|
|
selectable: Arc<Selectable<'a>>,
|
2023-01-16 09:53:52 +00:00
|
|
|
framable: Arc<Framable>,
|
|
|
|
iconizable: IconizableWrapper,
|
|
|
|
textable: TextableWrapper,
|
2024-04-26 06:28:05 +00:00
|
|
|
info_icon: IconizableWrapper,
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
_click_sound: Option<Arc<Audible>>,
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
_hover_sound: Option<Arc<Audible>>,
|
|
|
|
|
|
|
|
click_executable: Arc<Executable<()>>,
|
2025-03-04 10:37:45 +00:00
|
|
|
select_executable: Arc<Executable<(&'a mut GuiHandler<'a>, bool)>>,
|
2023-01-16 09:53:52 +00:00
|
|
|
on_select_executable: Arc<Executable<bool>>,
|
|
|
|
|
|
|
|
normal: FillType,
|
|
|
|
selected: FillType,
|
|
|
|
|
|
|
|
button_state: Mutex<ButtonState>,
|
|
|
|
select_mode: ButtonSelectMode,
|
|
|
|
|
|
|
|
visible: AtomicBool,
|
|
|
|
}
|
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
impl<'a> Button<'a> {
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn builder() -> ButtonBuilder {
|
|
|
|
ButtonBuilder {
|
|
|
|
icon: None,
|
|
|
|
margin: 0,
|
|
|
|
|
2024-05-17 13:12:48 +00:00
|
|
|
isolate: false,
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
text: String::new(),
|
|
|
|
text_color: Color::Black,
|
|
|
|
ratio: 0.7,
|
|
|
|
text_alignment: TextAlignment::Center,
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
click_sound: None,
|
|
|
|
#[cfg(feature = "audio")]
|
|
|
|
hover_sound: None,
|
|
|
|
|
|
|
|
button_select_mode: ButtonSelectMode::Bigger,
|
|
|
|
|
|
|
|
normal: None,
|
|
|
|
selected: None,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn select(&self, gui_handler: &mut GuiHandler<'a>) -> Result<()> {
|
|
|
|
self.selectable.select(gui_handler)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_callback<F>(&self, callback: F)
|
|
|
|
where
|
|
|
|
F: Fn() -> Result<()> + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
self.click_executable.set_callback(move |_| callback());
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_select_callback<F>(&self, callback: F)
|
|
|
|
where
|
|
|
|
F: Fn(bool) -> Result<()> + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
self.on_select_executable.set_callback(callback);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_custom_callback<F>(&self, callback: F)
|
|
|
|
where
|
|
|
|
F: Fn(ControllerButton) -> Result<bool> + Send + Sync + 'static,
|
|
|
|
{
|
|
|
|
self.selectable.set_custom_callback(callback);
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_text(&self, gui_handler: &mut GuiHandler<'_>, text: impl ToString) -> Result<()> {
|
|
|
|
self.textable.set_text(gui_handler, text, self.visible())
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_icon(&self, gui_handler: &mut GuiHandler<'_>, icon: &Arc<Image>) -> Result<()> {
|
|
|
|
self.iconizable.set_icon(gui_handler, icon, self.visible())
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_icon_margon(&self, gui_handler: &mut GuiHandler<'_>, margin: u32) -> Result<()> {
|
|
|
|
self.iconizable.set_margin(gui_handler, margin)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn clear_icon(&self, gui_handler: &mut GuiHandler<'_>) -> Result<()> {
|
|
|
|
self.iconizable.clear_icon(gui_handler, self.visible())
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn icon(&self) -> Result<Option<Arc<Image>>> {
|
|
|
|
self.iconizable.icon()
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_info_icon(
|
|
|
|
&self,
|
|
|
|
gui_handler: &mut GuiHandler<'_>,
|
|
|
|
button: &Arc<Image>,
|
|
|
|
) -> Result<()> {
|
|
|
|
self.info_icon.set_icon(gui_handler, button, self.visible())
|
2024-04-04 15:57:50 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn clear_info_icon(&self, gui_handler: &mut GuiHandler<'_>) -> Result<()> {
|
|
|
|
self.info_icon.clear_icon(gui_handler, self.visible())
|
2024-04-04 16:17:11 +00:00
|
|
|
}
|
|
|
|
|
2023-01-16 09:53:52 +00:00
|
|
|
pub fn text(&self) -> Result<Option<String>> {
|
|
|
|
self.textable.text()
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_text_color(
|
|
|
|
&self,
|
|
|
|
gui_handler: &mut GuiHandler<'_>,
|
|
|
|
text_color: Color,
|
|
|
|
) -> Result<()> {
|
|
|
|
self.textable.set_text_color(gui_handler, text_color)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
pub fn try_from(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler: &mut GuiHandler<'_>,
|
2025-03-03 19:01:17 +00:00
|
|
|
button_info: &ButtonInfo,
|
|
|
|
) -> Result<Arc<Self>> {
|
2024-05-17 13:12:48 +00:00
|
|
|
let mut button_builder = Button::builder()
|
|
|
|
.set_select_mode(button_info.select_mode)
|
|
|
|
.set_isolate(button_info.isolate);
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if let Some(normal) = button_info.normal.clone() {
|
|
|
|
button_builder = button_builder.set_normal(normal);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(selected) = button_info.selected.clone() {
|
|
|
|
button_builder = button_builder.set_selected(selected);
|
|
|
|
}
|
|
|
|
|
|
|
|
button_builder = button_builder
|
|
|
|
.set_text(button_info.text.read().unwrap().clone())
|
|
|
|
.set_text_color(button_info.text_color)
|
|
|
|
.set_text_alignment(button_info.text_alignment);
|
|
|
|
|
|
|
|
if let Some(ratio) = button_info.text_ratio {
|
|
|
|
button_builder = button_builder.set_text_ratio(ratio);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
2025-02-28 05:26:57 +00:00
|
|
|
if let Some(click_sound) = &button_info.click_sound {
|
|
|
|
button_builder = button_builder.set_click_sound(click_sound.clone());
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "audio")]
|
2025-02-28 05:26:57 +00:00
|
|
|
if let Some(hover_sound) = &button_info.hover_sound {
|
|
|
|
button_builder = button_builder.set_hover_sound(hover_sound.clone());
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
button_builder = button_builder.set_icon(
|
|
|
|
button_info
|
|
|
|
.icon
|
|
|
|
.clone()
|
|
|
|
.map(|icon| {
|
|
|
|
Image::from_file(AssetPath::from((
|
2025-02-28 05:48:19 +00:00
|
|
|
gui_handler
|
|
|
|
.resource_base_path()
|
|
|
|
.ok_or(anyhow!("resource base path not set!"))?
|
|
|
|
.full_path(),
|
2023-01-16 09:53:52 +00:00
|
|
|
icon,
|
|
|
|
)))?
|
|
|
|
.attach_sampler(Sampler::nearest_sampler().build(gui_handler.device())?)
|
|
|
|
.build(gui_handler.device(), gui_handler.queue())
|
|
|
|
})
|
|
|
|
.transpose()?,
|
|
|
|
button_info.margin,
|
|
|
|
);
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let button = button_builder.build(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(button)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
impl<'a> GuiElementTraits<'a> for Button<'a> {
|
|
|
|
fn gridable(&self) -> Option<&dyn Gridable<'a>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visibility(&self) -> Option<&dyn Visibility> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
fn downcast(&'a self) -> Option<GuiElement<'a>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(GuiElement::Button(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
impl<'a> Visibility for Button<'a> {
|
2023-01-16 09:53:52 +00:00
|
|
|
fn visible(&self) -> bool {
|
|
|
|
self.visible.load(SeqCst)
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
fn set_visibility(&self, gui_handler: &mut GuiHandler<'_>, visibility: bool) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
if visibility != self.visible.load(SeqCst) {
|
|
|
|
self.visible.store(visibility, SeqCst);
|
|
|
|
|
|
|
|
if visibility {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.add(gui_handler)?;
|
|
|
|
self.selectable.add(gui_handler)?;
|
|
|
|
self.hoverable.add(gui_handler)?;
|
|
|
|
self.clickable.add(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.textable.enable(gui_handler)?;
|
|
|
|
self.iconizable.enable(gui_handler)?;
|
|
|
|
self.info_icon.enable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
match *self.button_state.lock().unwrap() {
|
2025-03-04 10:37:45 +00:00
|
|
|
ButtonState::Normal => self.normal.enable(gui_handler)?,
|
|
|
|
ButtonState::Selected => self.selected.enable(gui_handler)?,
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
} else {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.disable_base(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
impl<'a> Gridable<'a> for Button<'a> {
|
2023-01-16 09:53:52 +00:00
|
|
|
fn set_frame(
|
|
|
|
&self,
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler: &mut GuiHandler<'_>,
|
2023-01-16 09:53:52 +00:00
|
|
|
x: i32,
|
|
|
|
y: i32,
|
|
|
|
w: u32,
|
|
|
|
h: u32,
|
|
|
|
vert_align: VerticalAlign,
|
|
|
|
hori_align: HorizontalAlign,
|
|
|
|
) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable
|
|
|
|
.set_frame(gui_handler, x, y, w, h, vert_align, hori_align);
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.normal.update_frame(gui_handler)?;
|
|
|
|
self.selected.update_frame(gui_handler)?;
|
|
|
|
self.textable.update(gui_handler)?;
|
|
|
|
self.iconizable.update_frame(gui_handler)?;
|
|
|
|
self.info_icon.update_frame(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if self.select_mode == ButtonSelectMode::Bigger {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.modify_hovered_vbo(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
fn selectable(&self) -> Option<&Arc<Selectable<'a>>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(&self.selectable)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
"Button"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_layer(&self, layer: i32) -> Result<()> {
|
|
|
|
self.clickable.set_ui_layer(layer);
|
|
|
|
self.hoverable.set_ui_layer(layer);
|
|
|
|
self.selectable.set_ui_layer(layer);
|
|
|
|
self.framable.set_ui_layer(layer);
|
|
|
|
self.iconizable.set_ui_layer(layer)?;
|
|
|
|
self.textable.set_ui_layer(layer)?;
|
2024-05-17 12:29:22 +00:00
|
|
|
self.info_icon.set_ui_layer(layer)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
self.normal.set_ui_layer(layer);
|
|
|
|
self.selected.set_ui_layer(layer);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2024-04-21 05:41:08 +00:00
|
|
|
|
|
|
|
fn position_extent(&self) -> (i32, i32, u32, u32) {
|
|
|
|
self.framable.position()
|
|
|
|
}
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// private
|
2025-03-03 19:01:17 +00:00
|
|
|
impl<'a> Button<'a> {
|
2023-01-16 09:53:52 +00:00
|
|
|
// fn create_hovered_changed_callback(button: Arc<Button>) -> Result<()> {
|
|
|
|
// let button_weak = Arc::downgrade(&button);
|
|
|
|
|
|
|
|
// let hovered_changed = Box::new(move |_| {
|
|
|
|
// if let Some(button) = button_weak.upgrade() {
|
|
|
|
// if !button.clickable.clicked() {
|
|
|
|
// if button.hoverable.hovered() {
|
|
|
|
// button.set_button_state(ButtonState::Selected)?;
|
|
|
|
// } else {
|
|
|
|
// button.set_button_state(ButtonState::Normal)?;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// Ok(())
|
|
|
|
// });
|
|
|
|
|
|
|
|
// button
|
|
|
|
// .hoverable
|
|
|
|
// .set_hovered_changed_callback(Some(hovered_changed))
|
|
|
|
// }
|
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
fn create_clicked_changed_callback(button: Arc<Button<'a>>) {
|
2023-01-16 09:53:52 +00:00
|
|
|
let button_weak = Arc::downgrade(&button);
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let clicked_changed = Box::new(move |gui_handler| {
|
2023-01-16 09:53:52 +00:00
|
|
|
if let Some(button) = button_weak.upgrade() {
|
|
|
|
if button.clickable.clicked() {
|
2025-03-04 10:37:45 +00:00
|
|
|
button.hoverable.set_hovered(gui_handler, false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
} else {
|
|
|
|
// if let Some(displayable) = displayable_weak.upgrade() {
|
|
|
|
// displayable.update_frame()?;
|
|
|
|
// }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
});
|
|
|
|
|
|
|
|
button
|
|
|
|
.clickable
|
|
|
|
.set_clicked_changed_callback(Some(clicked_changed));
|
|
|
|
}
|
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
fn create_selected_changed_callback(button: Arc<Button<'a>>) {
|
2023-01-16 09:53:52 +00:00
|
|
|
let button_weak = Arc::downgrade(&button);
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let selected_changed = move |(gui_handler, selected)| {
|
2023-01-16 09:53:52 +00:00
|
|
|
if let Some(button) = button_weak.upgrade() {
|
|
|
|
if selected {
|
2025-03-04 10:37:45 +00:00
|
|
|
button.set_button_state(gui_handler, ButtonState::Selected)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
} else {
|
2025-03-04 10:37:45 +00:00
|
|
|
button.set_button_state(gui_handler, ButtonState::Normal)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
};
|
|
|
|
|
|
|
|
button.select_executable.set_callback(selected_changed);
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
fn modify_hovered_vbo(&self, gui_handler: &GuiHandler<'_>) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
assert!(
|
|
|
|
self.framable.is_framed(),
|
|
|
|
"button frame needs to be defined before hovering can be calculated"
|
|
|
|
);
|
|
|
|
|
|
|
|
let offset = (self.framable.bottom() as i32 - self.framable.top()) as f32 * 0.1;
|
|
|
|
|
|
|
|
match &self.selected.inner {
|
|
|
|
InnerFillType::Image(displayable) => {
|
|
|
|
let buffer = displayable.buffer();
|
|
|
|
let mut frame = buffer.map_complete()?;
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let ortho = gui_handler.ortho();
|
2023-01-16 09:53:52 +00:00
|
|
|
let left = self.framable.left() as f32;
|
|
|
|
let right = self.framable.right() as f32;
|
|
|
|
let top = self.framable.top() as f32;
|
|
|
|
let bottom = self.framable.bottom() as f32;
|
|
|
|
|
2024-05-13 13:03:54 +00:00
|
|
|
frame[0].position = (ortho * vec4(left - offset, bottom + offset, 0.0, 1.0)).xy();
|
|
|
|
frame[1].position = (ortho * vec4(right + offset, bottom + offset, 0.0, 1.0)).xy();
|
|
|
|
frame[2].position = (ortho * vec4(right + offset, top - offset, 0.0, 1.0)).xy();
|
|
|
|
frame[3].position = (ortho * vec4(right + offset, top - offset, 0.0, 1.0)).xy();
|
|
|
|
frame[4].position = (ortho * vec4(left - offset, top - offset, 0.0, 1.0)).xy();
|
|
|
|
frame[5].position = (ortho * vec4(left - offset, bottom + offset, 0.0, 1.0)).xy();
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
frame[0].texture_coordinates = vec2(0.0, 1.0);
|
|
|
|
frame[1].texture_coordinates = vec2(1.0, 1.0);
|
|
|
|
frame[2].texture_coordinates = vec2(1.0, 0.0);
|
|
|
|
frame[3].texture_coordinates = vec2(1.0, 0.0);
|
|
|
|
frame[4].texture_coordinates = vec2(0.0, 0.0);
|
|
|
|
frame[5].texture_coordinates = vec2(0.0, 1.0);
|
|
|
|
}
|
|
|
|
InnerFillType::Color(colorable) => {
|
|
|
|
let buffer = colorable.buffer();
|
|
|
|
let mut frame = buffer.map_complete()?;
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let ortho = gui_handler.ortho();
|
2023-01-16 09:53:52 +00:00
|
|
|
let left = self.framable.left() as f32;
|
|
|
|
let right = self.framable.right() as f32;
|
|
|
|
let top = self.framable.top() as f32;
|
|
|
|
let bottom = self.framable.bottom() as f32;
|
|
|
|
|
2024-05-13 13:03:54 +00:00
|
|
|
frame[0].position = (ortho * vec4(left - offset, bottom + offset, 0.0, 1.0)).xy();
|
|
|
|
frame[1].position = (ortho * vec4(right + offset, bottom + offset, 0.0, 1.0)).xy();
|
|
|
|
frame[2].position = (ortho * vec4(right + offset, top - offset, 0.0, 1.0)).xy();
|
|
|
|
frame[3].position = (ortho * vec4(right + offset, top - offset, 0.0, 1.0)).xy();
|
|
|
|
frame[4].position = (ortho * vec4(left - offset, top - offset, 0.0, 1.0)).xy();
|
|
|
|
frame[5].position = (ortho * vec4(left - offset, bottom + offset, 0.0, 1.0)).xy();
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
fn disable_base(&self, gui_handler: &mut GuiHandler<'_>) -> Result<()> {
|
|
|
|
self.framable.delete(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.selectable.delete(gui_handler)?;
|
|
|
|
self.hoverable.delete(gui_handler)?;
|
|
|
|
self.clickable.delete(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.textable.disable(gui_handler)?;
|
|
|
|
self.iconizable.disable(gui_handler)?;
|
|
|
|
self.info_icon.disable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.normal.disable(gui_handler)?;
|
|
|
|
self.selected.disable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2025-03-04 10:37:45 +00:00
|
|
|
fn set_button_state(
|
|
|
|
&self,
|
|
|
|
gui_handler: &mut GuiHandler<'_>,
|
|
|
|
button_state: ButtonState,
|
|
|
|
) -> Result<()> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let mut current_button_state = self.button_state.lock().unwrap();
|
|
|
|
|
|
|
|
if button_state == *current_button_state {
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
*current_button_state = button_state;
|
|
|
|
|
|
|
|
match button_state {
|
|
|
|
ButtonState::Normal => {
|
|
|
|
if self.visible() {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.normal.enable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.selected.disable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
ButtonState::Selected => {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.normal.disable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
if self.visible() {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.selected.enable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
2024-05-25 07:42:05 +00:00
|
|
|
|
2025-03-03 19:01:17 +00:00
|
|
|
impl<'a> Deref for Button<'a> {
|
|
|
|
type Target = Arc<Selectable<'a>>;
|
2024-05-25 07:42:05 +00:00
|
|
|
|
|
|
|
fn deref(&self) -> &Self::Target {
|
|
|
|
&self.selectable
|
|
|
|
}
|
|
|
|
}
|