1use super::items::{build_item_from_value, control_to_value, SettingControl, SettingItem};
7use super::schema::{SettingSchema, SettingType};
8use crate::view::controls::{FocusState, TextInputState};
9use serde_json::Value;
10
11#[derive(Debug, Clone)]
13pub struct EntryDialogState {
14 pub entry_key: String,
16 pub map_path: String,
18 pub title: String,
20 pub is_new: bool,
22 pub items: Vec<SettingItem>,
24 pub selected_item: usize,
26 pub sub_focus: Option<usize>,
28 pub editing_text: bool,
30 pub focused_button: usize,
32 pub focus_on_buttons: bool,
34 pub delete_requested: bool,
36 pub scroll_offset: usize,
38 pub viewport_height: usize,
40 pub hover_item: Option<usize>,
42 pub hover_button: Option<usize>,
44 pub original_value: Value,
46 pub first_editable_index: usize,
49 pub no_delete: bool,
51}
52
53impl EntryDialogState {
54 pub fn from_schema(
60 key: String,
61 value: &Value,
62 schema: &SettingSchema,
63 map_path: &str,
64 is_new: bool,
65 no_delete: bool,
66 ) -> Self {
67 let mut items = Vec::new();
68
69 let key_item = SettingItem {
71 path: "__key__".to_string(),
72 name: "Key".to_string(),
73 description: Some("unique identifier for this entry".to_string()),
74 control: SettingControl::Text(TextInputState::new("Key").with_value(&key)),
75 default: None,
76 modified: false,
77 layer_source: crate::config_io::ConfigLayer::System,
78 read_only: !is_new, is_auto_managed: false,
80 section: None,
81 is_section_start: false,
82 layout_width: 0,
83 };
84 items.push(key_item);
85
86 if let SettingType::Object { properties } = &schema.setting_type {
88 for prop in properties {
89 let field_name = prop.path.trim_start_matches('/');
90 let field_value = value.get(field_name);
91 let item = build_item_from_value(prop, field_value);
92 items.push(item);
93 }
94 }
95
96 items.sort_by_key(|item| !item.read_only);
98
99 let first_editable_index = items
101 .iter()
102 .position(|item| !item.read_only)
103 .unwrap_or(items.len());
104
105 let focus_on_buttons = first_editable_index >= items.len();
107 let selected_item = if focus_on_buttons {
108 0
109 } else {
110 first_editable_index
111 };
112
113 let title = if is_new {
114 format!("Add {}", schema.name)
115 } else {
116 format!("Edit {}", schema.name)
117 };
118
119 Self {
120 entry_key: key,
121 map_path: map_path.to_string(),
122 title,
123 is_new,
124 items,
125 selected_item,
126 sub_focus: None,
127 editing_text: false,
128 focused_button: 0,
129 focus_on_buttons,
130 delete_requested: false,
131 scroll_offset: 0,
132 viewport_height: 20, hover_item: None,
134 hover_button: None,
135 original_value: value.clone(),
136 first_editable_index,
137 no_delete,
138 }
139 }
140
141 pub fn for_array_item(
145 index: Option<usize>,
146 value: &Value,
147 schema: &SettingSchema,
148 array_path: &str,
149 is_new: bool,
150 ) -> Self {
151 let mut items = Vec::new();
152
153 if let SettingType::Object { properties } = &schema.setting_type {
155 for prop in properties {
156 let field_name = prop.path.trim_start_matches('/');
157 let field_value = value.get(field_name);
158 let item = build_item_from_value(prop, field_value);
159 items.push(item);
160 }
161 }
162
163 items.sort_by_key(|item| !item.read_only);
165
166 let first_editable_index = items
168 .iter()
169 .position(|item| !item.read_only)
170 .unwrap_or(items.len());
171
172 let focus_on_buttons = first_editable_index >= items.len();
174 let selected_item = if focus_on_buttons {
175 0
176 } else {
177 first_editable_index
178 };
179
180 let title = if is_new {
181 format!("Add {}", schema.name)
182 } else {
183 format!("Edit {}", schema.name)
184 };
185
186 Self {
187 entry_key: index.map_or(String::new(), |i| i.to_string()),
188 map_path: array_path.to_string(),
189 title,
190 is_new,
191 items,
192 selected_item,
193 sub_focus: None,
194 editing_text: false,
195 focused_button: 0,
196 focus_on_buttons,
197 delete_requested: false,
198 scroll_offset: 0,
199 viewport_height: 20,
200 hover_item: None,
201 hover_button: None,
202 original_value: value.clone(),
203 first_editable_index,
204 no_delete: false, }
206 }
207
208 pub fn get_key(&self) -> String {
210 for item in &self.items {
212 if item.path == "__key__" {
213 if let SettingControl::Text(state) = &item.control {
214 return state.value.clone();
215 }
216 }
217 }
218 self.entry_key.clone()
219 }
220
221 pub fn button_count(&self) -> usize {
223 if self.is_new || self.no_delete {
224 2 } else {
226 3
227 }
228 }
229
230 pub fn to_value(&self) -> Value {
232 let mut obj = serde_json::Map::new();
233
234 for item in &self.items {
235 if item.path == "__key__" {
237 continue;
238 }
239
240 let field_name = item.path.trim_start_matches('/');
241 let value = control_to_value(&item.control);
242 obj.insert(field_name.to_string(), value);
243 }
244
245 Value::Object(obj)
246 }
247
248 pub fn current_item(&self) -> Option<&SettingItem> {
250 if self.focus_on_buttons {
251 None
252 } else {
253 self.items.get(self.selected_item)
254 }
255 }
256
257 pub fn current_item_mut(&mut self) -> Option<&mut SettingItem> {
259 if self.focus_on_buttons {
260 None
261 } else {
262 self.items.get_mut(self.selected_item)
263 }
264 }
265
266 pub fn focus_next(&mut self) {
268 if self.editing_text {
269 return; }
271
272 if self.focus_on_buttons {
273 if self.focused_button + 1 < self.button_count() {
274 self.focused_button += 1;
276 } else {
277 if self.first_editable_index < self.items.len() {
279 self.focus_on_buttons = false;
280 self.selected_item = self.first_editable_index;
281 }
282 }
284 } else {
285 let array_nav_result = self.items.get(self.selected_item).and_then(|item| {
287 if let SettingControl::ObjectArray(state) = &item.control {
288 match state.focused_index {
290 Some(idx) if idx + 1 < state.bindings.len() => {
291 Some(true)
293 }
294 Some(_) => {
295 Some(true)
297 }
298 None => {
299 Some(false)
301 }
302 }
303 } else {
304 None
305 }
306 });
307
308 match array_nav_result {
309 Some(true) => {
310 if let Some(item) = self.items.get_mut(self.selected_item) {
312 if let SettingControl::ObjectArray(state) = &mut item.control {
313 state.focus_next();
314 }
315 }
316 }
317 Some(false) => {
318 if self.selected_item + 1 < self.items.len() {
320 self.selected_item += 1;
321 self.sub_focus = None;
322 self.init_object_array_focus();
324 } else {
325 self.focus_on_buttons = true;
326 self.focused_button = 0;
327 }
328 }
329 None => {
330 if self.selected_item + 1 < self.items.len() {
333 self.selected_item += 1;
334 self.sub_focus = None;
335 self.init_object_array_focus();
337 } else {
338 self.focus_on_buttons = true;
339 self.focused_button = 0;
340 }
341 }
342 }
343 }
344
345 self.update_focus_states();
346 self.ensure_selected_visible(self.viewport_height);
347 }
348
349 pub fn focus_prev(&mut self) {
351 if self.editing_text {
352 return; }
354
355 if self.focus_on_buttons {
356 if self.focused_button > 0 {
357 self.focused_button -= 1;
358 } else {
359 if self.first_editable_index < self.items.len() {
361 self.focus_on_buttons = false;
362 self.selected_item = self.items.len().saturating_sub(1);
363 }
364 }
366 } else {
367 let array_nav_result = self.items.get(self.selected_item).and_then(|item| {
369 if let SettingControl::ObjectArray(state) = &item.control {
370 match state.focused_index {
372 None => {
373 if !state.bindings.is_empty() {
375 Some(true)
376 } else {
377 Some(false) }
379 }
380 Some(0) => {
381 Some(false)
383 }
384 Some(_) => {
385 Some(true)
387 }
388 }
389 } else {
390 None
391 }
392 });
393
394 match array_nav_result {
395 Some(true) => {
396 if let Some(item) = self.items.get_mut(self.selected_item) {
398 if let SettingControl::ObjectArray(state) = &mut item.control {
399 state.focus_prev();
400 }
401 }
402 }
403 Some(false) => {
404 if self.selected_item > self.first_editable_index {
406 self.selected_item -= 1;
407 self.sub_focus = None;
408 self.init_object_array_focus_end();
410 } else {
411 self.focus_on_buttons = true;
413 self.focused_button = self.button_count().saturating_sub(1);
414 }
415 }
416 None => {
417 if self.selected_item > self.first_editable_index {
420 self.selected_item -= 1;
421 self.sub_focus = None;
422 self.init_object_array_focus_end();
424 } else {
425 self.focus_on_buttons = true;
427 self.focused_button = self.button_count().saturating_sub(1);
428 }
429 }
430 }
431 }
432
433 self.update_focus_states();
434 self.ensure_selected_visible(self.viewport_height);
435 }
436
437 fn init_object_array_focus(&mut self) {
439 if let Some(item) = self.items.get_mut(self.selected_item) {
440 if let SettingControl::ObjectArray(state) = &mut item.control {
441 if !state.bindings.is_empty() {
443 state.focused_index = Some(0);
444 }
445 }
446 }
447 }
448
449 fn init_object_array_focus_end(&mut self) {
451 if let Some(item) = self.items.get_mut(self.selected_item) {
452 if let SettingControl::ObjectArray(state) = &mut item.control {
453 state.focused_index = None;
455 }
456 }
457 }
458
459 pub fn sub_focus_next(&mut self) {
461 if let Some(item) = self.items.get(self.selected_item) {
462 let max_sub = match &item.control {
463 SettingControl::TextList(state) => state.items.len(), SettingControl::Map(state) => state.entries.len(), _ => 0,
466 };
467
468 if max_sub > 0 {
469 let current = self.sub_focus.unwrap_or(0);
470 if current < max_sub {
471 self.sub_focus = Some(current + 1);
472 } else {
473 self.sub_focus = None;
475 self.focus_next();
476 }
477 } else {
478 self.focus_next();
479 }
480 } else {
481 self.focus_next();
482 }
483 }
484
485 pub fn sub_focus_prev(&mut self) {
487 if let Some(sub) = self.sub_focus {
488 if sub > 0 {
489 self.sub_focus = Some(sub - 1);
490 } else {
491 self.sub_focus = None;
492 }
493 } else {
494 self.focus_prev();
495 }
496 }
497
498 pub fn update_focus_states(&mut self) {
500 for (idx, item) in self.items.iter_mut().enumerate() {
501 let state = if !self.focus_on_buttons && idx == self.selected_item {
502 FocusState::Focused
503 } else {
504 FocusState::Normal
505 };
506
507 match &mut item.control {
508 SettingControl::Toggle(s) => s.focus = state,
509 SettingControl::Number(s) => s.focus = state,
510 SettingControl::Dropdown(s) => s.focus = state,
511 SettingControl::Text(s) => s.focus = state,
512 SettingControl::TextList(s) => s.focus = state,
513 SettingControl::Map(s) => s.focus = state,
514 SettingControl::ObjectArray(s) => s.focus = state,
515 SettingControl::Json(s) => s.focus = state,
516 SettingControl::Complex { .. } => {}
517 }
518 }
519 }
520
521 pub fn total_content_height(&self) -> usize {
523 let items_height: usize = self
524 .items
525 .iter()
526 .map(|item| item.control.control_height() as usize)
527 .sum();
528 let separator_height =
530 if self.first_editable_index > 0 && self.first_editable_index < self.items.len() {
531 1
532 } else {
533 0
534 };
535 items_height + separator_height
536 }
537
538 pub fn selected_item_offset(&self) -> usize {
540 let items_offset: usize = self
541 .items
542 .iter()
543 .take(self.selected_item)
544 .map(|item| item.control.control_height() as usize)
545 .sum();
546 let separator_offset = if self.first_editable_index > 0
548 && self.first_editable_index < self.items.len()
549 && self.selected_item >= self.first_editable_index
550 {
551 1
552 } else {
553 0
554 };
555 items_offset + separator_offset
556 }
557
558 pub fn selected_item_height(&self) -> usize {
560 self.items
561 .get(self.selected_item)
562 .map(|item| item.control.control_height() as usize)
563 .unwrap_or(1)
564 }
565
566 pub fn ensure_selected_visible(&mut self, viewport_height: usize) {
568 if self.focus_on_buttons {
569 let total = self.total_content_height();
571 if total > viewport_height {
572 self.scroll_offset = total.saturating_sub(viewport_height);
573 }
574 return;
575 }
576
577 let item_start = self.selected_item_offset();
578 let item_end = item_start + self.selected_item_height();
579
580 if item_start < self.scroll_offset {
582 self.scroll_offset = item_start;
583 }
584 else if item_end > self.scroll_offset + viewport_height {
586 self.scroll_offset = item_end.saturating_sub(viewport_height);
587 }
588 }
589
590 pub fn ensure_cursor_visible(&mut self) {
595 if !self.editing_text || self.focus_on_buttons {
596 return;
597 }
598
599 let cursor_row = if let Some(item) = self.items.get(self.selected_item) {
601 if let SettingControl::Json(state) = &item.control {
602 state.cursor_pos().0
603 } else {
604 return; }
606 } else {
607 return;
608 };
609
610 let item_offset = self.selected_item_offset();
613 let cursor_content_row = item_offset + 1 + cursor_row;
614
615 let viewport_height = self.viewport_height;
616
617 if cursor_content_row < self.scroll_offset {
619 self.scroll_offset = cursor_content_row;
620 }
621 else if cursor_content_row >= self.scroll_offset + viewport_height {
623 self.scroll_offset = cursor_content_row.saturating_sub(viewport_height) + 1;
624 }
625 }
626
627 pub fn scroll_up(&mut self) {
629 self.scroll_offset = self.scroll_offset.saturating_sub(1);
630 }
631
632 pub fn scroll_down(&mut self, viewport_height: usize) {
634 let max_scroll = self.total_content_height().saturating_sub(viewport_height);
635 if self.scroll_offset < max_scroll {
636 self.scroll_offset += 1;
637 }
638 }
639
640 pub fn scroll_to_ratio(&mut self, ratio: f32) {
644 let max_scroll = self
645 .total_content_height()
646 .saturating_sub(self.viewport_height);
647 let new_offset = (ratio * max_scroll as f32).round() as usize;
648 self.scroll_offset = new_offset.min(max_scroll);
649 }
650
651 pub fn start_editing(&mut self) {
653 if let Some(item) = self.current_item_mut() {
654 if item.read_only {
656 return;
657 }
658 match &mut item.control {
659 SettingControl::Text(state) => {
660 state.cursor = state.value.len();
662 self.editing_text = true;
663 }
664 SettingControl::TextList(state) => {
665 state.focus_new_item();
667 self.editing_text = true;
668 }
669 SettingControl::Number(state) => {
670 state.start_editing();
671 self.editing_text = true;
672 }
673 SettingControl::Json(_) => {
674 self.editing_text = true;
676 }
677 _ => {}
678 }
679 }
680 }
681
682 pub fn stop_editing(&mut self) {
684 if let Some(item) = self.current_item_mut() {
685 if let SettingControl::Number(state) = &mut item.control {
686 state.cancel_editing();
687 }
688 }
689 self.editing_text = false;
690 }
691
692 pub fn insert_char(&mut self, c: char) {
694 if !self.editing_text {
695 return;
696 }
697 if let Some(item) = self.current_item_mut() {
698 match &mut item.control {
699 SettingControl::Text(state) => {
700 state.insert(c);
701 }
702 SettingControl::TextList(state) => {
703 state.insert(c);
704 }
705 SettingControl::Number(state) => {
706 state.insert_char(c);
707 }
708 SettingControl::Json(state) => {
709 state.insert(c);
710 }
711 _ => {}
712 }
713 }
714 }
715
716 pub fn insert_str(&mut self, s: &str) {
717 if !self.editing_text {
718 return;
719 }
720 if let Some(item) = self.current_item_mut() {
721 match &mut item.control {
722 SettingControl::Text(state) => {
723 state.insert_str(s);
724 }
725 SettingControl::TextList(state) => {
726 state.insert_str(s);
727 }
728 SettingControl::Number(state) => {
729 for c in s.chars() {
730 state.insert_char(c);
731 }
732 }
733 SettingControl::Json(state) => {
734 state.insert_str(s);
735 }
736 _ => {}
737 }
738 }
739 }
740
741 pub fn backspace(&mut self) {
743 if !self.editing_text {
744 return;
745 }
746 if let Some(item) = self.current_item_mut() {
747 match &mut item.control {
748 SettingControl::Text(state) => {
749 state.backspace();
750 }
751 SettingControl::TextList(state) => {
752 state.backspace();
753 }
754 SettingControl::Number(state) => {
755 state.backspace();
756 }
757 SettingControl::Json(state) => {
758 state.backspace();
759 }
760 _ => {}
761 }
762 }
763 }
764
765 pub fn cursor_left(&mut self) {
767 if !self.editing_text {
768 return;
769 }
770 if let Some(item) = self.current_item_mut() {
771 match &mut item.control {
772 SettingControl::Text(state) => {
773 state.move_left();
774 }
775 SettingControl::TextList(state) => {
776 state.move_left();
777 }
778 SettingControl::Json(state) => {
779 state.move_left();
780 }
781 _ => {}
782 }
783 }
784 }
785
786 pub fn cursor_left_selecting(&mut self) {
788 if !self.editing_text {
789 return;
790 }
791 if let Some(item) = self.current_item_mut() {
792 if let SettingControl::Json(state) = &mut item.control {
793 state.editor.move_left_selecting();
794 }
795 }
796 }
797
798 pub fn cursor_right(&mut self) {
800 if !self.editing_text {
801 return;
802 }
803 if let Some(item) = self.current_item_mut() {
804 match &mut item.control {
805 SettingControl::Text(state) => {
806 state.move_right();
807 }
808 SettingControl::TextList(state) => {
809 state.move_right();
810 }
811 SettingControl::Json(state) => {
812 state.move_right();
813 }
814 _ => {}
815 }
816 }
817 }
818
819 pub fn cursor_right_selecting(&mut self) {
821 if !self.editing_text {
822 return;
823 }
824 if let Some(item) = self.current_item_mut() {
825 if let SettingControl::Json(state) = &mut item.control {
826 state.editor.move_right_selecting();
827 }
828 }
829 }
830
831 pub fn cursor_up(&mut self) {
833 if !self.editing_text {
834 return;
835 }
836 if let Some(item) = self.current_item_mut() {
837 if let SettingControl::Json(state) = &mut item.control {
838 state.move_up();
839 }
840 }
841 self.ensure_cursor_visible();
842 }
843
844 pub fn cursor_up_selecting(&mut self) {
846 if !self.editing_text {
847 return;
848 }
849 if let Some(item) = self.current_item_mut() {
850 if let SettingControl::Json(state) = &mut item.control {
851 state.editor.move_up_selecting();
852 }
853 }
854 self.ensure_cursor_visible();
855 }
856
857 pub fn cursor_down(&mut self) {
859 if !self.editing_text {
860 return;
861 }
862 if let Some(item) = self.current_item_mut() {
863 if let SettingControl::Json(state) = &mut item.control {
864 state.move_down();
865 }
866 }
867 self.ensure_cursor_visible();
868 }
869
870 pub fn cursor_down_selecting(&mut self) {
872 if !self.editing_text {
873 return;
874 }
875 if let Some(item) = self.current_item_mut() {
876 if let SettingControl::Json(state) = &mut item.control {
877 state.editor.move_down_selecting();
878 }
879 }
880 self.ensure_cursor_visible();
881 }
882
883 pub fn insert_newline(&mut self) {
885 if !self.editing_text {
886 return;
887 }
888 if let Some(item) = self.current_item_mut() {
889 if let SettingControl::Json(state) = &mut item.control {
890 state.insert('\n');
891 }
892 }
893 }
894
895 pub fn revert_json_and_stop(&mut self) {
897 if let Some(item) = self.current_item_mut() {
898 if let SettingControl::Json(state) = &mut item.control {
899 state.revert();
900 }
901 }
902 self.editing_text = false;
903 }
904
905 pub fn is_editing_json(&self) -> bool {
907 if !self.editing_text {
908 return false;
909 }
910 self.current_item()
911 .map(|item| matches!(&item.control, SettingControl::Json(_)))
912 .unwrap_or(false)
913 }
914
915 pub fn toggle_bool(&mut self) {
917 if let Some(item) = self.current_item_mut() {
918 if item.read_only {
920 return;
921 }
922 if let SettingControl::Toggle(state) = &mut item.control {
923 state.checked = !state.checked;
924 }
925 }
926 }
927
928 pub fn toggle_dropdown(&mut self) {
930 if let Some(item) = self.current_item_mut() {
931 if item.read_only {
933 return;
934 }
935 if let SettingControl::Dropdown(state) = &mut item.control {
936 state.open = !state.open;
937 }
938 }
939 }
940
941 pub fn dropdown_prev(&mut self) {
943 if let Some(item) = self.current_item_mut() {
944 if let SettingControl::Dropdown(state) = &mut item.control {
945 if state.open {
946 state.select_prev();
947 }
948 }
949 }
950 }
951
952 pub fn dropdown_next(&mut self) {
954 if let Some(item) = self.current_item_mut() {
955 if let SettingControl::Dropdown(state) = &mut item.control {
956 if state.open {
957 state.select_next();
958 }
959 }
960 }
961 }
962
963 pub fn dropdown_confirm(&mut self) {
965 if let Some(item) = self.current_item_mut() {
966 if let SettingControl::Dropdown(state) = &mut item.control {
967 state.open = false;
968 }
969 }
970 }
971
972 pub fn increment_number(&mut self) {
974 if let Some(item) = self.current_item_mut() {
975 if item.read_only {
977 return;
978 }
979 if let SettingControl::Number(state) = &mut item.control {
980 state.increment();
981 }
982 }
983 }
984
985 pub fn decrement_number(&mut self) {
987 if let Some(item) = self.current_item_mut() {
988 if item.read_only {
990 return;
991 }
992 if let SettingControl::Number(state) = &mut item.control {
993 state.decrement();
994 }
995 }
996 }
997
998 pub fn delete_list_item(&mut self) {
1000 if let Some(item) = self.current_item_mut() {
1001 if let SettingControl::TextList(state) = &mut item.control {
1002 if let Some(idx) = state.focused_item {
1004 state.remove_item(idx);
1005 }
1006 }
1007 }
1008 }
1009
1010 pub fn delete(&mut self) {
1012 if !self.editing_text {
1013 return;
1014 }
1015 if let Some(item) = self.current_item_mut() {
1016 match &mut item.control {
1017 SettingControl::Text(state) => {
1018 state.delete();
1019 }
1020 SettingControl::TextList(state) => {
1021 state.delete();
1022 }
1023 SettingControl::Json(state) => {
1024 state.delete();
1025 }
1026 _ => {}
1027 }
1028 }
1029 }
1030
1031 pub fn cursor_home(&mut self) {
1033 if !self.editing_text {
1034 return;
1035 }
1036 if let Some(item) = self.current_item_mut() {
1037 match &mut item.control {
1038 SettingControl::Text(state) => {
1039 state.move_home();
1040 }
1041 SettingControl::TextList(state) => {
1042 state.move_home();
1043 }
1044 SettingControl::Json(state) => {
1045 state.move_home();
1046 }
1047 _ => {}
1048 }
1049 }
1050 }
1051
1052 pub fn cursor_end(&mut self) {
1054 if !self.editing_text {
1055 return;
1056 }
1057 if let Some(item) = self.current_item_mut() {
1058 match &mut item.control {
1059 SettingControl::Text(state) => {
1060 state.move_end();
1061 }
1062 SettingControl::TextList(state) => {
1063 state.move_end();
1064 }
1065 SettingControl::Json(state) => {
1066 state.move_end();
1067 }
1068 _ => {}
1069 }
1070 }
1071 }
1072
1073 pub fn select_all(&mut self) {
1075 if !self.editing_text {
1076 return;
1077 }
1078 if let Some(item) = self.current_item_mut() {
1079 if let SettingControl::Json(state) = &mut item.control {
1080 state.select_all();
1081 }
1082 }
1084 }
1085
1086 pub fn selected_text(&self) -> Option<String> {
1088 if !self.editing_text {
1089 return None;
1090 }
1091 if let Some(item) = self.current_item() {
1092 if let SettingControl::Json(state) = &item.control {
1093 return state.selected_text();
1094 }
1095 }
1096 None
1097 }
1098
1099 pub fn is_editing(&self) -> bool {
1101 self.editing_text
1102 || self
1103 .current_item()
1104 .map(|item| {
1105 matches!(
1106 &item.control,
1107 SettingControl::Dropdown(s) if s.open
1108 )
1109 })
1110 .unwrap_or(false)
1111 }
1112}
1113
1114#[cfg(test)]
1115mod tests {
1116 use super::*;
1117
1118 fn create_test_schema() -> SettingSchema {
1119 SettingSchema {
1120 path: "/test".to_string(),
1121 name: "Test".to_string(),
1122 description: Some("Test schema".to_string()),
1123 setting_type: SettingType::Object {
1124 properties: vec![
1125 SettingSchema {
1126 path: "/enabled".to_string(),
1127 name: "Enabled".to_string(),
1128 description: Some("Enable this".to_string()),
1129 setting_type: SettingType::Boolean,
1130 default: Some(serde_json::json!(true)),
1131 read_only: false,
1132 section: None,
1133 },
1134 SettingSchema {
1135 path: "/command".to_string(),
1136 name: "Command".to_string(),
1137 description: Some("Command to run".to_string()),
1138 setting_type: SettingType::String,
1139 default: Some(serde_json::json!("")),
1140 read_only: false,
1141 section: None,
1142 },
1143 ],
1144 },
1145 default: None,
1146 read_only: false,
1147 section: None,
1148 }
1149 }
1150
1151 #[test]
1152 fn from_schema_creates_key_item_first() {
1153 let schema = create_test_schema();
1154 let dialog = EntryDialogState::from_schema(
1155 "test".to_string(),
1156 &serde_json::json!({}),
1157 &schema,
1158 "/test",
1159 false,
1160 false,
1161 );
1162
1163 assert!(!dialog.items.is_empty());
1164 assert_eq!(dialog.items[0].path, "__key__");
1165 assert_eq!(dialog.items[0].name, "Key");
1166 }
1167
1168 #[test]
1169 fn from_schema_creates_items_from_properties() {
1170 let schema = create_test_schema();
1171 let dialog = EntryDialogState::from_schema(
1172 "test".to_string(),
1173 &serde_json::json!({"enabled": true, "command": "test-cmd"}),
1174 &schema,
1175 "/test",
1176 false,
1177 false,
1178 );
1179
1180 assert_eq!(dialog.items.len(), 3);
1182 assert_eq!(dialog.items[1].name, "Enabled");
1183 assert_eq!(dialog.items[2].name, "Command");
1184 }
1185
1186 #[test]
1187 fn get_key_returns_key_value() {
1188 let schema = create_test_schema();
1189 let dialog = EntryDialogState::from_schema(
1190 "mykey".to_string(),
1191 &serde_json::json!({}),
1192 &schema,
1193 "/test",
1194 false,
1195 false,
1196 );
1197
1198 assert_eq!(dialog.get_key(), "mykey");
1199 }
1200
1201 #[test]
1202 fn to_value_excludes_key() {
1203 let schema = create_test_schema();
1204 let dialog = EntryDialogState::from_schema(
1205 "test".to_string(),
1206 &serde_json::json!({"enabled": true, "command": "cmd"}),
1207 &schema,
1208 "/test",
1209 false,
1210 false,
1211 );
1212
1213 let value = dialog.to_value();
1214 assert!(value.get("__key__").is_none());
1215 assert!(value.get("enabled").is_some());
1216 }
1217
1218 #[test]
1219 fn focus_navigation_works() {
1220 let schema = create_test_schema();
1221 let mut dialog = EntryDialogState::from_schema(
1222 "test".to_string(),
1223 &serde_json::json!({}),
1224 &schema,
1225 "/test",
1226 false, false, );
1229
1230 assert_eq!(dialog.first_editable_index, 1);
1234 assert_eq!(dialog.selected_item, 1); assert!(!dialog.focus_on_buttons);
1236
1237 dialog.focus_next();
1238 assert_eq!(dialog.selected_item, 2); dialog.focus_next();
1241 assert!(dialog.focus_on_buttons); assert_eq!(dialog.focused_button, 0);
1243
1244 dialog.focus_prev();
1246 assert!(!dialog.focus_on_buttons);
1247 assert_eq!(dialog.selected_item, 2); dialog.focus_prev();
1250 assert_eq!(dialog.selected_item, 1); dialog.focus_prev();
1253 assert!(dialog.focus_on_buttons); }
1255
1256 #[test]
1257 fn button_count_differs_for_new_vs_existing() {
1258 let schema = create_test_schema();
1259
1260 let new_dialog = EntryDialogState::from_schema(
1261 "test".to_string(),
1262 &serde_json::json!({}),
1263 &schema,
1264 "/test",
1265 true,
1266 false,
1267 );
1268 assert_eq!(new_dialog.button_count(), 2); let existing_dialog = EntryDialogState::from_schema(
1271 "test".to_string(),
1272 &serde_json::json!({}),
1273 &schema,
1274 "/test",
1275 false,
1276 false, );
1278 assert_eq!(existing_dialog.button_count(), 3); let no_delete_dialog = EntryDialogState::from_schema(
1282 "test".to_string(),
1283 &serde_json::json!({}),
1284 &schema,
1285 "/test",
1286 false,
1287 true, );
1289 assert_eq!(no_delete_dialog.button_count(), 2); }
1291}