mittens_engine/engine/ecs/component/
text_input.rs1use 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}