Skip to main content

mittens_engine/engine/ecs/component/
text_input.rs

1use crate::engine::ecs::ComponentId;
2use crate::engine::ecs::component::Component;
3use crate::engine::ecs::{IntentValue, SignalEmitter};
4
5#[derive(Debug, Clone)]
6pub struct TextInputComponent {
7    pub text: String,
8    pub caret: usize,
9    pub focused: bool,
10    pub read_only: bool,
11    component: Option<ComponentId>,
12}
13
14impl TextInputComponent {
15    pub fn new(text: impl Into<String>) -> Self {
16        let text = text.into();
17        let caret = text.chars().count();
18        Self {
19            text,
20            caret,
21            focused: false,
22            read_only: false,
23            component: None,
24        }
25    }
26
27    pub fn set_text(&mut self, text: impl Into<String>) {
28        self.text = text.into();
29        self.clamp_caret();
30    }
31
32    pub fn clamp_caret(&mut self) {
33        self.caret = self.caret.min(self.text.chars().count());
34    }
35}
36
37impl Default for TextInputComponent {
38    fn default() -> Self {
39        Self::new("")
40    }
41}
42
43impl Component for TextInputComponent {
44    fn name(&self) -> &'static str {
45        "text_input"
46    }
47
48    fn set_id(&mut self, id: ComponentId) {
49        self.component = Some(id);
50    }
51
52    fn as_any(&self) -> &dyn std::any::Any {
53        self
54    }
55
56    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
57        self
58    }
59
60    fn init(&mut self, emit: &mut dyn SignalEmitter, component: ComponentId) {
61        let _ = self.component;
62        emit.push_intent_now(
63            component,
64            IntentValue::RegisterTextInput {
65                component_ids: vec![component],
66            },
67        );
68    }
69
70    fn encode(&self) -> std::collections::HashMap<String, serde_json::Value> {
71        let mut out = std::collections::HashMap::new();
72        out.insert(
73            "text".to_string(),
74            serde_json::Value::String(self.text.clone()),
75        );
76        if self.read_only {
77            out.insert("read_only".to_string(), serde_json::Value::Bool(true));
78        }
79        out
80    }
81
82    fn decode(
83        &mut self,
84        data: &std::collections::HashMap<String, serde_json::Value>,
85    ) -> Result<(), String> {
86        if let Some(serde_json::Value::String(text)) = data.get("text") {
87            self.text = text.clone();
88        }
89        if let Some(serde_json::Value::Bool(read_only)) = data.get("read_only") {
90            self.read_only = *read_only;
91        }
92        self.clamp_caret();
93        Ok(())
94    }
95
96    fn to_mms_ast(
97        &self,
98        _world: &crate::engine::ecs::World,
99    ) -> crate::scripting::ast::ComponentExpression {
100        use crate::engine::ecs::component::ce_helpers::*;
101        use crate::scripting::ast::{Expression, Statement};
102
103        let mut node = ce("TextInput");
104        if !self.text.is_empty() {
105            node.body
106                .statements
107                .push(Statement::Expression(Expression::String(self.text.clone())));
108        }
109        if self.read_only {
110            node = node.with_call("read_only", vec![b(true)]);
111        }
112        node
113    }
114}
115
116#[derive(Debug, Clone, Copy)]
117pub struct TextInputGlyphHitComponent {
118    pub text_input_root: ComponentId,
119    pub text_target: ComponentId,
120    pub char_index: usize,
121}
122
123impl Component for TextInputGlyphHitComponent {
124    fn name(&self) -> &'static str {
125        "text_input_glyph_hit"
126    }
127
128    fn set_id(&mut self, _id: ComponentId) {}
129
130    fn as_any(&self) -> &dyn std::any::Any {
131        self
132    }
133
134    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
135        self
136    }
137}