siglus_scene_vm/runtime/forms/
editbox.rs1use anyhow::Result;
2
3use super::prop_access;
4use crate::runtime::constants;
5use crate::runtime::globals::EditBoxListState;
6use crate::runtime::{CommandContext, Value};
7
8fn default_for_ret_form(ret_form: i32) -> Value {
9 if prop_access::ret_form_is_string(ret_form as i64) {
10 Value::Str(String::new())
11 } else {
12 Value::Int(0)
13 }
14}
15
16fn editbox_cnt(ctx: &CommandContext) -> usize {
17 ctx.tables
18 .gameexe
19 .as_ref()
20 .map(|cfg| cfg.indexed_count("EDITBOX"))
21 .unwrap_or(0)
22}
23
24fn is_array_code(elm_array: i32, code: i32) -> bool {
25 if elm_array < 0 {
26 return code != 0;
27 }
28 code == elm_array
29}
30
31fn is_editbox_like_chain(ctx: &CommandContext, form_id: u32, chain: &[i32]) -> bool {
32 if chain.is_empty() || chain[0] as u32 != form_id {
33 return false;
34 }
35 if chain.len() == 2 {
36 return !is_array_code(ctx.ids.elm_array, chain[1]);
37 }
38 chain.len() >= 3 && is_array_code(ctx.ids.elm_array, chain[1])
39}
40
41fn apply_exact_op(
42 form_id: u32,
43 list: &mut EditBoxListState,
44 idx: usize,
45 op: i32,
46 params: &[Value],
47 ret_form: i32,
48 screen_w: i32,
49 screen_h: i32,
50) -> (Option<Value>, Option<Option<(u32, usize)>>) {
51 if idx >= list.boxes.len() {
52 let r = if ret_form != 0 {
53 Some(default_for_ret_form(ret_form))
54 } else {
55 None
56 };
57 return (r, None);
58 }
59
60 let eb = &mut list.boxes[idx];
61 match op {
62 x if x == constants::elm_value::EDITBOX_CREATE => {
63 let x = params.get(0).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
64 let y = params.get(1).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
65 let w = params.get(2).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
66 let h = params.get(3).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
67 let moji_size = params.get(4).and_then(|v| v.as_i64()).unwrap_or(0) as i32;
68 eb.create_like(x, y, w, h, moji_size, screen_w, screen_h);
69 eb.update_rect(screen_w, screen_h);
70 eb.frame(0);
71 (None, Some(Some((form_id, idx))))
72 }
73 x if x == constants::elm_value::EDITBOX_DESTROY => {
74 eb.destroy_like();
75 (None, Some(None))
76 }
77 x if x == constants::elm_value::EDITBOX_SET_TEXT => {
78 eb.set_text_like(
79 params
80 .iter()
81 .find_map(|v| v.as_str())
82 .unwrap_or("")
83 .to_string(),
84 );
85 (None, None)
86 }
87 x if x == constants::elm_value::EDITBOX_GET_TEXT => {
88 (Some(Value::Str(eb.text.clone())), None)
89 }
90 x if x == constants::elm_value::EDITBOX_SET_FOCUS => {
91 if eb.created {
92 (None, Some(Some((form_id, idx))))
93 } else {
94 (None, None)
95 }
96 }
97 x if x == constants::elm_value::EDITBOX_CLEAR_INPUT => {
98 eb.clear_input();
99 (None, None)
100 }
101 x if x == constants::elm_value::EDITBOX_CHECK_DECIDED => {
102 (Some(Value::Int(if eb.is_decided() { 1 } else { 0 })), None)
103 }
104 x if x == constants::elm_value::EDITBOX_CHECK_CANCELED => {
105 (Some(Value::Int(if eb.is_canceled() { 1 } else { 0 })), None)
106 }
107 _ => {
108 let r = if ret_form != 0 {
109 Some(default_for_ret_form(ret_form))
110 } else {
111 None
112 };
113 (r, None)
114 }
115 }
116}
117
118pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
119 let Some((chain_pos, chain)) =
120 prop_access::parse_element_chain_ctx(ctx, form_id, args).map(|(i, ch)| (i, ch.to_vec()))
121 else {
122 return Ok(false);
123 };
124 if !is_editbox_like_chain(ctx, form_id, &chain) {
125 return Ok(false);
126 }
127
128 let (meta_al_id, _meta_ret_form, rhs_meta) =
129 prop_access::infer_assign_and_ret_ctx(ctx, chain_pos, args);
130 if chain.len() >= 4 && is_array_code(ctx.ids.elm_array, chain[1]) && meta_al_id == Some(1) {
131 let rhs = rhs_meta.as_ref();
132 let idx = chain.get(2).copied().unwrap_or(0).max(0) as usize;
133 let op = chain[3];
134 let cnt = editbox_cnt(ctx);
135 let list = ctx
136 .globals
137 .editbox_lists
138 .entry(form_id)
139 .or_insert_with(|| EditBoxListState::new(cnt));
140 list.ensure_size(cnt);
141 let params = match rhs {
142 Some(Value::Str(s)) => vec![Value::Str(s.clone())],
143 Some(v) => vec![v.clone()],
144 None => Vec::new(),
145 };
146 let (_ret, focus_req) = apply_exact_op(
147 form_id,
148 list,
149 idx,
150 op,
151 ¶ms,
152 0,
153 ctx.screen_w as i32,
154 ctx.screen_h as i32,
155 );
156 if let Some(req) = focus_req {
157 match req {
158 Some(tgt) => ctx.globals.focused_editbox = Some(tgt),
159 None => {
160 if ctx.globals.focused_editbox == Some((form_id, idx)) {
161 ctx.globals.focused_editbox = None;
162 }
163 }
164 }
165 }
166 return Ok(true);
167 }
168
169 let params = prop_access::script_args(args, chain_pos);
170 let ret_form = crate::runtime::forms::prop_access::current_vm_meta(ctx)
171 .1
172 .unwrap_or(0) as i32;
173 let elm_array = ctx.ids.elm_array;
174
175 let idx_for_focus = chain.get(2).copied().unwrap_or(0).max(0) as usize;
176 let cnt = editbox_cnt(ctx);
177 let (handled, ret, focus_req): (bool, Option<Value>, Option<Option<(u32, usize)>>) = 'blk: {
178 let list = ctx
179 .globals
180 .editbox_lists
181 .entry(form_id)
182 .or_insert_with(|| EditBoxListState::new(cnt));
183 list.ensure_size(cnt);
184
185 if chain.len() == 2 && !is_array_code(elm_array, chain[1]) {
186 let op = chain[1];
187 if op == constants::elm_value::EDITBOXLIST_CLEAR_INPUT && ret_form == 0 {
188 for eb in list.boxes.iter_mut() {
189 eb.clear_input();
190 }
191 break 'blk (true, None, None);
192 }
193 if ret_form != 0 {
194 break 'blk (true, Some(Value::Int(list.boxes.len() as i64)), None);
195 }
196 break 'blk (true, None, None);
197 }
198
199 if chain.len() < 4 || !is_array_code(elm_array, chain[1]) {
200 let r = if ret_form != 0 {
201 Some(default_for_ret_form(ret_form))
202 } else {
203 None
204 };
205 break 'blk (true, r, None);
206 }
207
208 let idx = chain.get(2).copied().unwrap_or(0).max(0) as usize;
209 let op = chain[3];
210 let (r, fr) = apply_exact_op(
211 form_id,
212 list,
213 idx,
214 op,
215 params,
216 ret_form,
217 ctx.screen_w as i32,
218 ctx.screen_h as i32,
219 );
220 break 'blk (true, r, fr);
221 };
222
223 if let Some(v) = ret {
224 ctx.push(v);
225 }
226 if let Some(fr) = focus_req {
227 match fr {
228 Some(tgt) => ctx.globals.focused_editbox = Some(tgt),
229 None => {
230 if ctx.globals.focused_editbox == Some((form_id, idx_for_focus)) {
231 ctx.globals.focused_editbox = None;
232 }
233 }
234 }
235 }
236 Ok(handled)
237}