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 };
83 items.push(key_item);
84
85 if let SettingType::Object { properties } = &schema.setting_type {
87 for prop in properties {
88 let field_name = prop.path.trim_start_matches('/');
89 let field_value = value.get(field_name);
90 let item = build_item_from_value(prop, field_value);
91 items.push(item);
92 }
93 }
94
95 items.sort_by_key(|item| !item.read_only);
97
98 let first_editable_index = items
100 .iter()
101 .position(|item| !item.read_only)
102 .unwrap_or(items.len());
103
104 let focus_on_buttons = first_editable_index >= items.len();
106 let selected_item = if focus_on_buttons {
107 0
108 } else {
109 first_editable_index
110 };
111
112 let title = if is_new {
113 format!("Add {}", schema.name)
114 } else {
115 format!("Edit {}", schema.name)
116 };
117
118 Self {
119 entry_key: key,
120 map_path: map_path.to_string(),
121 title,
122 is_new,
123 items,
124 selected_item,
125 sub_focus: None,
126 editing_text: false,
127 focused_button: 0,
128 focus_on_buttons,
129 delete_requested: false,
130 scroll_offset: 0,
131 viewport_height: 20, hover_item: None,
133 hover_button: None,
134 original_value: value.clone(),
135 first_editable_index,
136 no_delete,
137 }
138 }
139
140 pub fn for_array_item(
144 index: Option<usize>,
145 value: &Value,
146 schema: &SettingSchema,
147 array_path: &str,
148 is_new: bool,
149 ) -> Self {
150 let mut items = Vec::new();
151
152 if let SettingType::Object { properties } = &schema.setting_type {
154 for prop in properties {
155 let field_name = prop.path.trim_start_matches('/');
156 let field_value = value.get(field_name);
157 let item = build_item_from_value(prop, field_value);
158 items.push(item);
159 }
160 }
161
162 items.sort_by_key(|item| !item.read_only);
164
165 let first_editable_index = items
167 .iter()
168 .position(|item| !item.read_only)
169 .unwrap_or(items.len());
170
171 let focus_on_buttons = first_editable_index >= items.len();
173 let selected_item = if focus_on_buttons {
174 0
175 } else {
176 first_editable_index
177 };
178
179 let title = if is_new {
180 format!("Add {}", schema.name)
181 } else {
182 format!("Edit {}", schema.name)
183 };
184
185 Self {
186 entry_key: index.map_or(String::new(), |i| i.to_string()),
187 map_path: array_path.to_string(),
188 title,
189 is_new,
190 items,
191 selected_item,
192 sub_focus: None,
193 editing_text: false,
194 focused_button: 0,
195 focus_on_buttons,
196 delete_requested: false,
197 scroll_offset: 0,
198 viewport_height: 20,
199 hover_item: None,
200 hover_button: None,
201 original_value: value.clone(),
202 first_editable_index,
203 no_delete: false, }
205 }
206
207 pub fn get_key(&self) -> String {
209 for item in &self.items {
211 if item.path == "__key__" {
212 if let SettingControl::Text(state) = &item.control {
213 return state.value.clone();
214 }
215 }
216 }
217 self.entry_key.clone()
218 }
219
220 pub fn button_count(&self) -> usize {
222 if self.is_new || self.no_delete {
223 2 } else {
225 3
226 }
227 }
228
229 pub fn to_value(&self) -> Value {
231 let mut obj = serde_json::Map::new();
232
233 for item in &self.items {
234 if item.path == "__key__" {
236 continue;
237 }
238
239 let field_name = item.path.trim_start_matches('/');
240 let value = control_to_value(&item.control);
241 obj.insert(field_name.to_string(), value);
242 }
243
244 Value::Object(obj)
245 }
246
247 pub fn current_item(&self) -> Option<&SettingItem> {
249 if self.focus_on_buttons {
250 None
251 } else {
252 self.items.get(self.selected_item)
253 }
254 }
255
256 pub fn current_item_mut(&mut self) -> Option<&mut SettingItem> {
258 if self.focus_on_buttons {
259 None
260 } else {
261 self.items.get_mut(self.selected_item)
262 }
263 }
264
265 pub fn focus_next(&mut self) {
267 if self.editing_text {
268 return; }
270
271 if self.focus_on_buttons {
272 if self.focused_button + 1 < self.button_count() {
273 self.focused_button += 1;
275 } else {
276 if self.first_editable_index < self.items.len() {
278 self.focus_on_buttons = false;
279 self.selected_item = self.first_editable_index;
280 }
281 }
283 } else {
284 let array_nav_result = self.items.get(self.selected_item).and_then(|item| {
286 if let SettingControl::ObjectArray(state) = &item.control {
287 match state.focused_index {
289 Some(idx) if idx + 1 < state.bindings.len() => {
290 Some(true)
292 }
293 Some(_) => {
294 Some(true)
296 }
297 None => {
298 Some(false)
300 }
301 }
302 } else {
303 None
304 }
305 });
306
307 match array_nav_result {
308 Some(true) => {
309 if let Some(item) = self.items.get_mut(self.selected_item) {
311 if let SettingControl::ObjectArray(state) = &mut item.control {
312 state.focus_next();
313 }
314 }
315 }
316 Some(false) => {
317 if self.selected_item + 1 < self.items.len() {
319 self.selected_item += 1;
320 self.sub_focus = None;
321 self.init_object_array_focus();
323 } else {
324 self.focus_on_buttons = true;
325 self.focused_button = 0;
326 }
327 }
328 None => {
329 if self.selected_item + 1 < self.items.len() {
332 self.selected_item += 1;
333 self.sub_focus = None;
334 self.init_object_array_focus();
336 } else {
337 self.focus_on_buttons = true;
338 self.focused_button = 0;
339 }
340 }
341 }
342 }
343
344 self.update_focus_states();
345 self.ensure_selected_visible(self.viewport_height);
346 }
347
348 pub fn focus_prev(&mut self) {
350 if self.editing_text {
351 return; }
353
354 if self.focus_on_buttons {
355 if self.focused_button > 0 {
356 self.focused_button -= 1;
357 } else {
358 if self.first_editable_index < self.items.len() {
360 self.focus_on_buttons = false;
361 self.selected_item = self.items.len().saturating_sub(1);
362 }
363 }
365 } else {
366 let array_nav_result = self.items.get(self.selected_item).and_then(|item| {
368 if let SettingControl::ObjectArray(state) = &item.control {
369 match state.focused_index {
371 None => {
372 if !state.bindings.is_empty() {
374 Some(true)
375 } else {
376 Some(false) }
378 }
379 Some(0) => {
380 Some(false)
382 }
383 Some(_) => {
384 Some(true)
386 }
387 }
388 } else {
389 None
390 }
391 });
392
393 match array_nav_result {
394 Some(true) => {
395 if let Some(item) = self.items.get_mut(self.selected_item) {
397 if let SettingControl::ObjectArray(state) = &mut item.control {
398 state.focus_prev();
399 }
400 }
401 }
402 Some(false) => {
403 if self.selected_item > self.first_editable_index {
405 self.selected_item -= 1;
406 self.sub_focus = None;
407 self.init_object_array_focus_end();
409 } else {
410 self.focus_on_buttons = true;
412 self.focused_button = self.button_count().saturating_sub(1);
413 }
414 }
415 None => {
416 if self.selected_item > self.first_editable_index {
419 self.selected_item -= 1;
420 self.sub_focus = None;
421 self.init_object_array_focus_end();
423 } else {
424 self.focus_on_buttons = true;
426 self.focused_button = self.button_count().saturating_sub(1);
427 }
428 }
429 }
430 }
431
432 self.update_focus_states();
433 self.ensure_selected_visible(self.viewport_height);
434 }
435
436 fn init_object_array_focus(&mut self) {
438 if let Some(item) = self.items.get_mut(self.selected_item) {
439 if let SettingControl::ObjectArray(state) = &mut item.control {
440 if !state.bindings.is_empty() {
442 state.focused_index = Some(0);
443 }
444 }
445 }
446 }
447
448 fn init_object_array_focus_end(&mut self) {
450 if let Some(item) = self.items.get_mut(self.selected_item) {
451 if let SettingControl::ObjectArray(state) = &mut item.control {
452 state.focused_index = None;
454 }
455 }
456 }
457
458 pub fn sub_focus_next(&mut self) {
460 if let Some(item) = self.items.get(self.selected_item) {
461 let max_sub = match &item.control {
462 SettingControl::TextList(state) => state.items.len(), SettingControl::Map(state) => state.entries.len(), _ => 0,
465 };
466
467 if max_sub > 0 {
468 let current = self.sub_focus.unwrap_or(0);
469 if current < max_sub {
470 self.sub_focus = Some(current + 1);
471 } else {
472 self.sub_focus = None;
474 self.focus_next();
475 }
476 } else {
477 self.focus_next();
478 }
479 } else {
480 self.focus_next();
481 }
482 }
483
484 pub fn sub_focus_prev(&mut self) {
486 if let Some(sub) = self.sub_focus {
487 if sub > 0 {
488 self.sub_focus = Some(sub - 1);
489 } else {
490 self.sub_focus = None;
491 }
492 } else {
493 self.focus_prev();
494 }
495 }
496
497 pub fn update_focus_states(&mut self) {
499 for (idx, item) in self.items.iter_mut().enumerate() {
500 let state = if !self.focus_on_buttons && idx == self.selected_item {
501 FocusState::Focused
502 } else {
503 FocusState::Normal
504 };
505
506 match &mut item.control {
507 SettingControl::Toggle(s) => s.focus = state,
508 SettingControl::Number(s) => s.focus = state,
509 SettingControl::Dropdown(s) => s.focus = state,
510 SettingControl::Text(s) => s.focus = state,
511 SettingControl::TextList(s) => s.focus = state,
512 SettingControl::Map(s) => s.focus = state,
513 SettingControl::ObjectArray(s) => s.focus = state,
514 SettingControl::Json(s) => s.focus = state,
515 SettingControl::Complex { .. } => {}
516 }
517 }
518 }
519
520 pub fn total_content_height(&self) -> usize {
522 let items_height: usize = self
523 .items
524 .iter()
525 .map(|item| item.control.control_height() as usize)
526 .sum();
527 let separator_height =
529 if self.first_editable_index > 0 && self.first_editable_index < self.items.len() {
530 1
531 } else {
532 0
533 };
534 items_height + separator_height
535 }
536
537 pub fn selected_item_offset(&self) -> usize {
539 let items_offset: usize = self
540 .items
541 .iter()
542 .take(self.selected_item)
543 .map(|item| item.control.control_height() as usize)
544 .sum();
545 let separator_offset = if self.first_editable_index > 0
547 && self.first_editable_index < self.items.len()
548 && self.selected_item >= self.first_editable_index
549 {
550 1
551 } else {
552 0
553 };
554 items_offset + separator_offset
555 }
556
557 pub fn selected_item_height(&self) -> usize {
559 self.items
560 .get(self.selected_item)
561 .map(|item| item.control.control_height() as usize)
562 .unwrap_or(1)
563 }
564
565 pub fn ensure_selected_visible(&mut self, viewport_height: usize) {
567 if self.focus_on_buttons {
568 let total = self.total_content_height();
570 if total > viewport_height {
571 self.scroll_offset = total.saturating_sub(viewport_height);
572 }
573 return;
574 }
575
576 let item_start = self.selected_item_offset();
577 let item_end = item_start + self.selected_item_height();
578
579 if item_start < self.scroll_offset {
581 self.scroll_offset = item_start;
582 }
583 else if item_end > self.scroll_offset + viewport_height {
585 self.scroll_offset = item_end.saturating_sub(viewport_height);
586 }
587 }
588
589 pub fn ensure_cursor_visible(&mut self) {
594 if !self.editing_text || self.focus_on_buttons {
595 return;
596 }
597
598 let cursor_row = if let Some(item) = self.items.get(self.selected_item) {
600 if let SettingControl::Json(state) = &item.control {
601 state.cursor_pos().0
602 } else {
603 return; }
605 } else {
606 return;
607 };
608
609 let item_offset = self.selected_item_offset();
612 let cursor_content_row = item_offset + 1 + cursor_row;
613
614 let viewport_height = self.viewport_height;
615
616 if cursor_content_row < self.scroll_offset {
618 self.scroll_offset = cursor_content_row;
619 }
620 else if cursor_content_row >= self.scroll_offset + viewport_height {
622 self.scroll_offset = cursor_content_row.saturating_sub(viewport_height) + 1;
623 }
624 }
625
626 pub fn scroll_up(&mut self) {
628 self.scroll_offset = self.scroll_offset.saturating_sub(1);
629 }
630
631 pub fn scroll_down(&mut self, viewport_height: usize) {
633 let max_scroll = self.total_content_height().saturating_sub(viewport_height);
634 if self.scroll_offset < max_scroll {
635 self.scroll_offset += 1;
636 }
637 }
638
639 pub fn scroll_to_ratio(&mut self, ratio: f32) {
643 let max_scroll = self
644 .total_content_height()
645 .saturating_sub(self.viewport_height);
646 let new_offset = (ratio * max_scroll as f32).round() as usize;
647 self.scroll_offset = new_offset.min(max_scroll);
648 }
649
650 pub fn start_editing(&mut self) {
652 if let Some(item) = self.current_item_mut() {
653 if item.read_only {
655 return;
656 }
657 match &mut item.control {
658 SettingControl::Text(state) => {
659 state.cursor = state.value.len();
661 self.editing_text = true;
662 }
663 SettingControl::TextList(state) => {
664 state.focus_new_item();
666 self.editing_text = true;
667 }
668 SettingControl::Number(state) => {
669 state.start_editing();
670 self.editing_text = true;
671 }
672 SettingControl::Json(_) => {
673 self.editing_text = true;
675 }
676 _ => {}
677 }
678 }
679 }
680
681 pub fn stop_editing(&mut self) {
683 if let Some(item) = self.current_item_mut() {
684 if let SettingControl::Number(state) = &mut item.control {
685 state.cancel_editing();
686 }
687 }
688 self.editing_text = false;
689 }
690
691 pub fn insert_char(&mut self, c: char) {
693 if !self.editing_text {
694 return;
695 }
696 if let Some(item) = self.current_item_mut() {
697 match &mut item.control {
698 SettingControl::Text(state) => {
699 state.insert(c);
700 }
701 SettingControl::TextList(state) => {
702 state.insert(c);
703 }
704 SettingControl::Number(state) => {
705 state.insert_char(c);
706 }
707 SettingControl::Json(state) => {
708 state.insert(c);
709 }
710 _ => {}
711 }
712 }
713 }
714
715 pub fn insert_str(&mut self, s: &str) {
716 if !self.editing_text {
717 return;
718 }
719 if let Some(item) = self.current_item_mut() {
720 match &mut item.control {
721 SettingControl::Text(state) => {
722 state.insert_str(s);
723 }
724 SettingControl::TextList(state) => {
725 state.insert_str(s);
726 }
727 SettingControl::Number(state) => {
728 for c in s.chars() {
729 state.insert_char(c);
730 }
731 }
732 SettingControl::Json(state) => {
733 state.insert_str(s);
734 }
735 _ => {}
736 }
737 }
738 }
739
740 pub fn backspace(&mut self) {
742 if !self.editing_text {
743 return;
744 }
745 if let Some(item) = self.current_item_mut() {
746 match &mut item.control {
747 SettingControl::Text(state) => {
748 state.backspace();
749 }
750 SettingControl::TextList(state) => {
751 state.backspace();
752 }
753 SettingControl::Number(state) => {
754 state.backspace();
755 }
756 SettingControl::Json(state) => {
757 state.backspace();
758 }
759 _ => {}
760 }
761 }
762 }
763
764 pub fn cursor_left(&mut self) {
766 if !self.editing_text {
767 return;
768 }
769 if let Some(item) = self.current_item_mut() {
770 match &mut item.control {
771 SettingControl::Text(state) => {
772 state.move_left();
773 }
774 SettingControl::TextList(state) => {
775 state.move_left();
776 }
777 SettingControl::Json(state) => {
778 state.move_left();
779 }
780 _ => {}
781 }
782 }
783 }
784
785 pub fn cursor_left_selecting(&mut self) {
787 if !self.editing_text {
788 return;
789 }
790 if let Some(item) = self.current_item_mut() {
791 if let SettingControl::Json(state) = &mut item.control {
792 state.editor.move_left_selecting();
793 }
794 }
795 }
796
797 pub fn cursor_right(&mut self) {
799 if !self.editing_text {
800 return;
801 }
802 if let Some(item) = self.current_item_mut() {
803 match &mut item.control {
804 SettingControl::Text(state) => {
805 state.move_right();
806 }
807 SettingControl::TextList(state) => {
808 state.move_right();
809 }
810 SettingControl::Json(state) => {
811 state.move_right();
812 }
813 _ => {}
814 }
815 }
816 }
817
818 pub fn cursor_right_selecting(&mut self) {
820 if !self.editing_text {
821 return;
822 }
823 if let Some(item) = self.current_item_mut() {
824 if let SettingControl::Json(state) = &mut item.control {
825 state.editor.move_right_selecting();
826 }
827 }
828 }
829
830 pub fn cursor_up(&mut self) {
832 if !self.editing_text {
833 return;
834 }
835 if let Some(item) = self.current_item_mut() {
836 if let SettingControl::Json(state) = &mut item.control {
837 state.move_up();
838 }
839 }
840 self.ensure_cursor_visible();
841 }
842
843 pub fn cursor_up_selecting(&mut self) {
845 if !self.editing_text {
846 return;
847 }
848 if let Some(item) = self.current_item_mut() {
849 if let SettingControl::Json(state) = &mut item.control {
850 state.editor.move_up_selecting();
851 }
852 }
853 self.ensure_cursor_visible();
854 }
855
856 pub fn cursor_down(&mut self) {
858 if !self.editing_text {
859 return;
860 }
861 if let Some(item) = self.current_item_mut() {
862 if let SettingControl::Json(state) = &mut item.control {
863 state.move_down();
864 }
865 }
866 self.ensure_cursor_visible();
867 }
868
869 pub fn cursor_down_selecting(&mut self) {
871 if !self.editing_text {
872 return;
873 }
874 if let Some(item) = self.current_item_mut() {
875 if let SettingControl::Json(state) = &mut item.control {
876 state.editor.move_down_selecting();
877 }
878 }
879 self.ensure_cursor_visible();
880 }
881
882 pub fn insert_newline(&mut self) {
884 if !self.editing_text {
885 return;
886 }
887 if let Some(item) = self.current_item_mut() {
888 if let SettingControl::Json(state) = &mut item.control {
889 state.insert('\n');
890 }
891 }
892 }
893
894 pub fn revert_json_and_stop(&mut self) {
896 if let Some(item) = self.current_item_mut() {
897 if let SettingControl::Json(state) = &mut item.control {
898 state.revert();
899 }
900 }
901 self.editing_text = false;
902 }
903
904 pub fn is_editing_json(&self) -> bool {
906 if !self.editing_text {
907 return false;
908 }
909 self.current_item()
910 .map(|item| matches!(&item.control, SettingControl::Json(_)))
911 .unwrap_or(false)
912 }
913
914 pub fn toggle_bool(&mut self) {
916 if let Some(item) = self.current_item_mut() {
917 if item.read_only {
919 return;
920 }
921 if let SettingControl::Toggle(state) = &mut item.control {
922 state.checked = !state.checked;
923 }
924 }
925 }
926
927 pub fn toggle_dropdown(&mut self) {
929 if let Some(item) = self.current_item_mut() {
930 if item.read_only {
932 return;
933 }
934 if let SettingControl::Dropdown(state) = &mut item.control {
935 state.open = !state.open;
936 }
937 }
938 }
939
940 pub fn dropdown_prev(&mut self) {
942 if let Some(item) = self.current_item_mut() {
943 if let SettingControl::Dropdown(state) = &mut item.control {
944 if state.open {
945 state.select_prev();
946 }
947 }
948 }
949 }
950
951 pub fn dropdown_next(&mut self) {
953 if let Some(item) = self.current_item_mut() {
954 if let SettingControl::Dropdown(state) = &mut item.control {
955 if state.open {
956 state.select_next();
957 }
958 }
959 }
960 }
961
962 pub fn dropdown_confirm(&mut self) {
964 if let Some(item) = self.current_item_mut() {
965 if let SettingControl::Dropdown(state) = &mut item.control {
966 state.open = false;
967 }
968 }
969 }
970
971 pub fn increment_number(&mut self) {
973 if let Some(item) = self.current_item_mut() {
974 if item.read_only {
976 return;
977 }
978 if let SettingControl::Number(state) = &mut item.control {
979 state.increment();
980 }
981 }
982 }
983
984 pub fn decrement_number(&mut self) {
986 if let Some(item) = self.current_item_mut() {
987 if item.read_only {
989 return;
990 }
991 if let SettingControl::Number(state) = &mut item.control {
992 state.decrement();
993 }
994 }
995 }
996
997 pub fn delete_list_item(&mut self) {
999 if let Some(item) = self.current_item_mut() {
1000 if let SettingControl::TextList(state) = &mut item.control {
1001 if let Some(idx) = state.focused_item {
1003 state.remove_item(idx);
1004 }
1005 }
1006 }
1007 }
1008
1009 pub fn delete(&mut self) {
1011 if !self.editing_text {
1012 return;
1013 }
1014 if let Some(item) = self.current_item_mut() {
1015 match &mut item.control {
1016 SettingControl::Text(state) => {
1017 state.delete();
1018 }
1019 SettingControl::TextList(state) => {
1020 state.delete();
1021 }
1022 SettingControl::Json(state) => {
1023 state.delete();
1024 }
1025 _ => {}
1026 }
1027 }
1028 }
1029
1030 pub fn cursor_home(&mut self) {
1032 if !self.editing_text {
1033 return;
1034 }
1035 if let Some(item) = self.current_item_mut() {
1036 match &mut item.control {
1037 SettingControl::Text(state) => {
1038 state.move_home();
1039 }
1040 SettingControl::TextList(state) => {
1041 state.move_home();
1042 }
1043 SettingControl::Json(state) => {
1044 state.move_home();
1045 }
1046 _ => {}
1047 }
1048 }
1049 }
1050
1051 pub fn cursor_end(&mut self) {
1053 if !self.editing_text {
1054 return;
1055 }
1056 if let Some(item) = self.current_item_mut() {
1057 match &mut item.control {
1058 SettingControl::Text(state) => {
1059 state.move_end();
1060 }
1061 SettingControl::TextList(state) => {
1062 state.move_end();
1063 }
1064 SettingControl::Json(state) => {
1065 state.move_end();
1066 }
1067 _ => {}
1068 }
1069 }
1070 }
1071
1072 pub fn select_all(&mut self) {
1074 if !self.editing_text {
1075 return;
1076 }
1077 if let Some(item) = self.current_item_mut() {
1078 if let SettingControl::Json(state) = &mut item.control {
1079 state.select_all();
1080 }
1081 }
1083 }
1084
1085 pub fn is_editing(&self) -> bool {
1087 self.editing_text
1088 || self
1089 .current_item()
1090 .map(|item| {
1091 matches!(
1092 &item.control,
1093 SettingControl::Dropdown(s) if s.open
1094 )
1095 })
1096 .unwrap_or(false)
1097 }
1098}
1099
1100#[cfg(test)]
1101mod tests {
1102 use super::*;
1103
1104 fn create_test_schema() -> SettingSchema {
1105 SettingSchema {
1106 path: "/test".to_string(),
1107 name: "Test".to_string(),
1108 description: Some("Test schema".to_string()),
1109 setting_type: SettingType::Object {
1110 properties: vec![
1111 SettingSchema {
1112 path: "/enabled".to_string(),
1113 name: "Enabled".to_string(),
1114 description: Some("Enable this".to_string()),
1115 setting_type: SettingType::Boolean,
1116 default: Some(serde_json::json!(true)),
1117 read_only: false,
1118 section: None,
1119 },
1120 SettingSchema {
1121 path: "/command".to_string(),
1122 name: "Command".to_string(),
1123 description: Some("Command to run".to_string()),
1124 setting_type: SettingType::String,
1125 default: Some(serde_json::json!("")),
1126 read_only: false,
1127 section: None,
1128 },
1129 ],
1130 },
1131 default: None,
1132 read_only: false,
1133 section: None,
1134 }
1135 }
1136
1137 #[test]
1138 fn from_schema_creates_key_item_first() {
1139 let schema = create_test_schema();
1140 let dialog = EntryDialogState::from_schema(
1141 "test".to_string(),
1142 &serde_json::json!({}),
1143 &schema,
1144 "/test",
1145 false,
1146 false,
1147 );
1148
1149 assert!(!dialog.items.is_empty());
1150 assert_eq!(dialog.items[0].path, "__key__");
1151 assert_eq!(dialog.items[0].name, "Key");
1152 }
1153
1154 #[test]
1155 fn from_schema_creates_items_from_properties() {
1156 let schema = create_test_schema();
1157 let dialog = EntryDialogState::from_schema(
1158 "test".to_string(),
1159 &serde_json::json!({"enabled": true, "command": "test-cmd"}),
1160 &schema,
1161 "/test",
1162 false,
1163 false,
1164 );
1165
1166 assert_eq!(dialog.items.len(), 3);
1168 assert_eq!(dialog.items[1].name, "Enabled");
1169 assert_eq!(dialog.items[2].name, "Command");
1170 }
1171
1172 #[test]
1173 fn get_key_returns_key_value() {
1174 let schema = create_test_schema();
1175 let dialog = EntryDialogState::from_schema(
1176 "mykey".to_string(),
1177 &serde_json::json!({}),
1178 &schema,
1179 "/test",
1180 false,
1181 false,
1182 );
1183
1184 assert_eq!(dialog.get_key(), "mykey");
1185 }
1186
1187 #[test]
1188 fn to_value_excludes_key() {
1189 let schema = create_test_schema();
1190 let dialog = EntryDialogState::from_schema(
1191 "test".to_string(),
1192 &serde_json::json!({"enabled": true, "command": "cmd"}),
1193 &schema,
1194 "/test",
1195 false,
1196 false,
1197 );
1198
1199 let value = dialog.to_value();
1200 assert!(value.get("__key__").is_none());
1201 assert!(value.get("enabled").is_some());
1202 }
1203
1204 #[test]
1205 fn focus_navigation_works() {
1206 let schema = create_test_schema();
1207 let mut dialog = EntryDialogState::from_schema(
1208 "test".to_string(),
1209 &serde_json::json!({}),
1210 &schema,
1211 "/test",
1212 false, false, );
1215
1216 assert_eq!(dialog.first_editable_index, 1);
1220 assert_eq!(dialog.selected_item, 1); assert!(!dialog.focus_on_buttons);
1222
1223 dialog.focus_next();
1224 assert_eq!(dialog.selected_item, 2); dialog.focus_next();
1227 assert!(dialog.focus_on_buttons); assert_eq!(dialog.focused_button, 0);
1229
1230 dialog.focus_prev();
1232 assert!(!dialog.focus_on_buttons);
1233 assert_eq!(dialog.selected_item, 2); dialog.focus_prev();
1236 assert_eq!(dialog.selected_item, 1); dialog.focus_prev();
1239 assert!(dialog.focus_on_buttons); }
1241
1242 #[test]
1243 fn button_count_differs_for_new_vs_existing() {
1244 let schema = create_test_schema();
1245
1246 let new_dialog = EntryDialogState::from_schema(
1247 "test".to_string(),
1248 &serde_json::json!({}),
1249 &schema,
1250 "/test",
1251 true,
1252 false,
1253 );
1254 assert_eq!(new_dialog.button_count(), 2); let existing_dialog = EntryDialogState::from_schema(
1257 "test".to_string(),
1258 &serde_json::json!({}),
1259 &schema,
1260 "/test",
1261 false,
1262 false, );
1264 assert_eq!(existing_dialog.button_count(), 3); let no_delete_dialog = EntryDialogState::from_schema(
1268 "test".to_string(),
1269 &serde_json::json!({}),
1270 &schema,
1271 "/test",
1272 false,
1273 true, );
1275 assert_eq!(no_delete_dialog.button_count(), 2); }
1277}