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