2023-01-16 09:53:52 +00:00
|
|
|
use crate::{builder::validator::textfieldinfo::TextFieldInfo, prelude::*};
|
|
|
|
use anyhow::Result;
|
2025-03-04 17:18:08 +00:00
|
|
|
use ecs::World;
|
2023-01-16 11:58:59 +00:00
|
|
|
use utilities::prelude::*;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
use std::sync::{
|
2023-01-19 12:05:48 +00:00
|
|
|
Arc, RwLock,
|
2025-03-04 10:37:45 +00:00
|
|
|
atomic::{AtomicBool, Ordering::SeqCst},
|
2023-01-16 09:53:52 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
use super::fill_type::{FillType, InnerFillType};
|
|
|
|
|
|
|
|
pub struct TextFieldBuilder {
|
|
|
|
background: Option<FillTypeInfo>,
|
|
|
|
text_color: Color,
|
|
|
|
text: Option<String>,
|
|
|
|
text_ratio: f32,
|
|
|
|
text_alignment: TextAlignment,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextFieldBuilder {
|
|
|
|
pub fn set_background(mut self, background: impl Into<FillTypeInfo>) -> Self {
|
|
|
|
self.background = Some(background.into());
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_color(mut self, text_color: Color) -> Self {
|
|
|
|
self.text_color = text_color;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_alignment(mut self, text_alignment: TextAlignment) -> Self {
|
|
|
|
self.text_alignment = text_alignment;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text(mut self, text: impl Into<String>) -> Self {
|
|
|
|
self.text = Some(text.into());
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_ratio(mut self, ratio: f32) -> Self {
|
|
|
|
self.text_ratio = ratio;
|
|
|
|
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn build(self, gui_handler: &mut GuiHandler) -> Result<Arc<TextField>> {
|
2025-03-04 10:37:45 +00:00
|
|
|
let framable = Framable::new(gui_handler.width(), gui_handler.height(), false)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-19 12:05:48 +00:00
|
|
|
let background = RwLock::new(
|
2023-01-16 09:53:52 +00:00
|
|
|
self.background
|
|
|
|
.clone()
|
2025-03-04 10:37:45 +00:00
|
|
|
.map(|info| FillType::new(gui_handler, framable.clone(), info))
|
2023-01-16 09:53:52 +00:00
|
|
|
.transpose()?,
|
|
|
|
);
|
|
|
|
|
|
|
|
let textable = Textable::new(
|
2025-03-04 10:37:45 +00:00
|
|
|
gui_handler,
|
2023-01-16 09:53:52 +00:00
|
|
|
framable.clone(),
|
|
|
|
match self.text {
|
|
|
|
Some(text) => text,
|
|
|
|
None => "".to_string(),
|
|
|
|
},
|
|
|
|
self.text_ratio,
|
|
|
|
self.text_alignment,
|
|
|
|
self.text_color,
|
|
|
|
)?;
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let text_changed_executable = Executable::new();
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
let writeable = Writeable::new(textable.clone(), text_changed_executable.clone())?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
let text_field = Arc::new(TextField {
|
|
|
|
framable,
|
|
|
|
background,
|
|
|
|
writeable,
|
|
|
|
textable,
|
|
|
|
|
|
|
|
text_changed_executable,
|
|
|
|
|
|
|
|
visible: AtomicBool::new(false),
|
|
|
|
});
|
|
|
|
|
|
|
|
text_field.update_color_change();
|
|
|
|
|
|
|
|
Ok(text_field)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct TextField {
|
|
|
|
framable: Arc<Framable>,
|
|
|
|
writeable: Arc<Writeable>,
|
|
|
|
textable: Arc<Textable>,
|
|
|
|
|
|
|
|
text_changed_executable: Arc<Executable<Option<String>>>,
|
|
|
|
|
2023-01-19 12:05:48 +00:00
|
|
|
background: RwLock<Option<FillType>>,
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
visible: AtomicBool,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TextField {
|
|
|
|
pub fn builder() -> TextFieldBuilder {
|
|
|
|
TextFieldBuilder {
|
|
|
|
background: None,
|
|
|
|
text_color: Color::Black,
|
|
|
|
text: None,
|
|
|
|
text_ratio: 0.7,
|
|
|
|
text_alignment: TextAlignment::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn try_from(info: &TextFieldInfo, gui_handler: &mut GuiHandler) -> Result<Arc<Self>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
let text = info.text.read().unwrap().clone();
|
|
|
|
let color = info.text_color;
|
|
|
|
|
|
|
|
let mut text_field_builder = Self::builder()
|
|
|
|
.set_text_color(color)
|
|
|
|
.set_text_alignment(info.text_alignment);
|
|
|
|
|
|
|
|
if let Some(background_type) = &info.background_type {
|
|
|
|
text_field_builder = text_field_builder.set_background(background_type.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(ratio) = info.text_ratio {
|
|
|
|
text_field_builder = text_field_builder.set_text_ratio(ratio);
|
|
|
|
}
|
|
|
|
|
|
|
|
if !text.is_empty() {
|
|
|
|
text_field_builder = text_field_builder.set_text(text);
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
text_field_builder.build(gui_handler)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn update_color_change(self: &Arc<Self>) {
|
2023-01-19 12:05:48 +00:00
|
|
|
if let Some(background) = &*self.background.read().unwrap() {
|
2023-01-16 09:53:52 +00:00
|
|
|
if let InnerFillType::Color(colorable) = &background.inner {
|
|
|
|
let normal_color = colorable.color();
|
|
|
|
let focus_color = normal_color * 1.4;
|
|
|
|
|
|
|
|
let weak_self = Arc::downgrade(self);
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
self.writeable
|
|
|
|
.set_activation_changed(move |gui_handler, active| {
|
|
|
|
if let Some(me) = weak_self.upgrade() {
|
|
|
|
if active {
|
|
|
|
if let Some(background) = &*me.background.read().unwrap() {
|
|
|
|
if let InnerFillType::Color(colorable) = &background.inner {
|
|
|
|
colorable.set_color(gui_handler, focus_color)?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if let Some(background) = &*me.background.read().unwrap() {
|
2023-01-16 09:53:52 +00:00
|
|
|
if let InnerFillType::Color(colorable) = &background.inner {
|
2025-03-04 10:37:45 +00:00
|
|
|
colorable.set_color(gui_handler, normal_color)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
Ok(())
|
|
|
|
});
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn set_text_changed_callback<F>(&self, f: F)
|
|
|
|
where
|
2025-03-04 17:18:08 +00:00
|
|
|
F: Fn(&mut World, Option<String>) -> Result<()> + Send + Sync + 'static,
|
2023-01-16 09:53:52 +00:00
|
|
|
{
|
|
|
|
self.text_changed_executable.set_callback(f);
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn set_text_color(&self, gui_handler: &mut GuiHandler, text_color: Color) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.textable.set_text_color(gui_handler, text_color)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn set_text(&self, gui_handler: &mut GuiHandler, text: impl ToString) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.textable.set_text(gui_handler, text)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn text(&self) -> Option<String> {
|
|
|
|
let t = self.textable.text();
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
if t.is_empty() { None } else { Some(t) }
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 10:37:45 +00:00
|
|
|
pub fn set_background(
|
|
|
|
self: &Arc<Self>,
|
2025-03-04 11:25:02 +00:00
|
|
|
gui_handler: &mut GuiHandler,
|
2025-03-04 10:37:45 +00:00
|
|
|
background: impl Into<FillTypeInfo>,
|
|
|
|
) -> Result<()> {
|
|
|
|
super::set_background(
|
|
|
|
gui_handler,
|
|
|
|
self.visible(),
|
|
|
|
&self.framable,
|
|
|
|
&self.background,
|
|
|
|
background,
|
|
|
|
)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
|
|
|
self.update_color_change();
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn background_color(&self) -> Option<Color> {
|
2023-01-19 12:05:48 +00:00
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2023-01-16 09:53:52 +00:00
|
|
|
if let InnerFillType::Color(colorable) = &background.inner {
|
|
|
|
return Some(colorable.color());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn add_letter(&self, gui_handler: &mut GuiHandler, letter: char) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.writeable.add_letter(gui_handler, letter)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn remove_last(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.writeable.remove_last(gui_handler)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
pub fn focus_input(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.writeable.set_active(gui_handler)
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn disable_base(&self, gui_handler: &mut GuiHandler) -> Result<()> {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.delete(gui_handler)?;
|
|
|
|
self.textable.delete(gui_handler)?;
|
|
|
|
self.writeable.delete(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-19 12:05:48 +00:00
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.disable(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl GuiElementTraits for TextField {
|
|
|
|
fn gridable(&self) -> Option<&dyn Gridable> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn visibility(&self) -> Option<&dyn Visibility> {
|
|
|
|
Some(self)
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn downcast<'a>(&'a self) -> Option<GuiElement<'a>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
Some(GuiElement::TextField(self))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
impl Gridable for TextField {
|
2023-01-16 09:53:52 +00:00
|
|
|
fn set_frame(
|
|
|
|
&self,
|
2025-03-04 11:25:02 +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.textable.update_text(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-19 12:05:48 +00:00
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.update_frame(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +00:00
|
|
|
fn selectable(&self) -> Option<&Arc<Selectable>> {
|
2023-01-16 09:53:52 +00:00
|
|
|
None
|
|
|
|
}
|
|
|
|
|
|
|
|
fn type_name(&self) -> &str {
|
|
|
|
"Text Field"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn set_layer(&self, layer: i32) -> Result<()> {
|
|
|
|
assert!(!self.visible(), "can't change ui layer while visible");
|
|
|
|
|
|
|
|
self.framable.set_ui_layer(layer);
|
|
|
|
self.textable.set_ui_layer(layer);
|
|
|
|
self.writeable.set_ui_layer(layer);
|
|
|
|
|
2023-01-19 12:05:48 +00:00
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2023-01-16 09:53:52 +00:00
|
|
|
background.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
|
|
|
}
|
|
|
|
|
|
|
|
impl Visibility for TextField {
|
|
|
|
fn visible(&self) -> bool {
|
|
|
|
self.visible.load(SeqCst)
|
|
|
|
}
|
|
|
|
|
2025-03-04 11:25:02 +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() {
|
|
|
|
self.visible.store(visibility, SeqCst);
|
|
|
|
|
|
|
|
if visibility {
|
2025-03-04 10:37:45 +00:00
|
|
|
self.framable.add(gui_handler)?;
|
|
|
|
self.textable.add(gui_handler)?;
|
|
|
|
self.writeable.add(gui_handler)?;
|
2023-01-16 09:53:52 +00:00
|
|
|
|
2023-01-19 12:05:48 +00:00
|
|
|
if let Some(background) = self.background.read().unwrap().as_ref() {
|
2025-03-04 10:37:45 +00:00
|
|
|
background.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(())
|
|
|
|
}
|
|
|
|
}
|