datui_lib/app/keys/
picker_keys.rs1use crate::loading::open_options::OpenOptions;
5use crate::{App, AppEvent, Overlay, app::table_switch};
6use crossterm::event::{KeyCode, KeyEvent};
7
8#[derive(Default)]
10pub struct Pickers {
11 pub go_to_column: crate::widgets::ui::PickerState,
13 pub format_picker: crate::widgets::ui::PickerState,
15 pub table_picker: crate::widgets::ui::PickerState,
17 pub table_choices: Option<crate::app::table_switch::Tables>,
18}
19
20impl App {
21 pub(crate) fn open_go_to_column(&mut self) {
24 let Some(state) = self.data_table_state.as_ref() else {
25 return;
26 };
27 let names = state.get_column_order().to_vec();
28 if names.is_empty() {
29 return;
30 }
31 let at = state
32 .current_column()
33 .and_then(|current| names.iter().position(|n| n == current))
34 .unwrap_or(0);
35 self.pickers.go_to_column = crate::widgets::ui::PickerState::new(names);
36 self.pickers.go_to_column.select_original(at);
37 self.open_overlay(Overlay::GoToColumn);
38 }
39
40 pub(crate) fn go_to_column_key(&mut self, event: &KeyEvent) {
43 match event.code {
44 KeyCode::Esc => self.close_overlay(),
45 KeyCode::Enter => {
46 let Some(index) = self.pickers.go_to_column.selected_original() else {
47 return;
49 };
50 let name = self.pickers.go_to_column.items()[index].clone();
52 if let Some(state) = self.data_table_state.as_mut() {
53 state.go_to_column(&name);
54 }
55 self.close_overlay();
56 }
57 KeyCode::Up => self.pickers.go_to_column.move_up(),
58 KeyCode::Down => self.pickers.go_to_column.move_down(),
59 KeyCode::Backspace => self.pickers.go_to_column.backspace(),
60 KeyCode::Char(c) => self.pickers.go_to_column.filter_key(c, event.modifiers),
61 _ => {}
62 }
63 }
64
65 pub(crate) fn open_format_picker(&mut self) {
68 let Some(read) = self
69 .data_table_state
70 .as_ref()
71 .and_then(|s| s.format_read())
72 .cloned()
73 else {
74 return;
76 };
77 let mut names = vec![read.spec.name.clone()];
78 names.extend(read.also.iter().cloned());
79 for found in &self.formats.specs {
80 if found.spec.layout == read.spec.layout
81 && !found.spec.is_delimited()
82 && !names.contains(&found.spec.name)
83 {
84 names.push(found.spec.name.clone());
85 }
86 }
87 self.pickers.format_picker = crate::widgets::ui::PickerState::new(names);
88 self.open_overlay(Overlay::PickFormat);
89 }
90
91 pub(crate) fn format_picker_key(&mut self, event: &KeyEvent) -> Option<AppEvent> {
94 match event.code {
95 KeyCode::Esc => self.close_overlay(),
96 KeyCode::Enter => {
97 let index = self.pickers.format_picker.selected_original()?;
98 let name = self.pickers.format_picker.items()[index].clone();
99 self.close_overlay();
100 let current = self
101 .data_table_state
102 .as_ref()
103 .and_then(|s| s.format_read())
104 .map(|read| read.spec.name.clone());
105 if current.as_deref() == Some(name.as_str()) {
106 return None;
107 }
108 let (paths, options) = self.source.opened.clone()?;
109 let options = OpenOptions {
110 spec_name: Some(name),
111 spec_file: None,
112 spec_fetched: None,
113 table: None,
114 format_read: None,
115 sqlite: None,
116 format: None,
117 ..options
118 };
119 self.set_loading_phase("Scanning input", 10);
120 self.name_what_is_loading(paths[0].clone());
121 return Some(AppEvent::Open(paths, options));
122 }
123 KeyCode::Up => self.pickers.format_picker.move_up(),
124 KeyCode::Down => self.pickers.format_picker.move_down(),
125 KeyCode::Backspace => self.pickers.format_picker.backspace(),
126 KeyCode::Char(c) => self.pickers.format_picker.filter_key(c, event.modifiers),
127 _ => {}
128 }
129 None
130 }
131
132 pub fn sibling_tables(&self) -> Option<table_switch::Tables> {
135 let state = self.data_table_state.as_ref()?;
136 let (paths, options) = self.source.opened.as_ref()?;
137 table_switch::of(state, paths, options)
138 }
139
140 pub fn offers_other_tables(&self) -> bool {
142 match (self.data_table_state.as_ref(), self.source.opened.as_ref()) {
143 (Some(state), Some((paths, options))) => table_switch::several(state, paths, options),
144 _ => false,
145 }
146 }
147
148 pub(crate) fn open_table_picker(&mut self) {
151 let Some(tables) = self.sibling_tables().filter(table_switch::Tables::several) else {
152 self.flash_note("Only one table here".to_string());
153 return;
154 };
155 let labels = tables.tables.iter().map(|t| t.label.clone()).collect();
156 self.pickers.table_picker = crate::widgets::ui::PickerState::new(labels);
157 if let Some(at) = tables.current {
158 self.pickers.table_picker.select_original(at);
159 }
160 self.pickers.table_choices = Some(tables);
161 self.open_overlay(Overlay::PickTable);
162 }
163
164 pub(crate) fn table_picker_key(&mut self, event: &KeyEvent) -> Option<AppEvent> {
167 match event.code {
168 KeyCode::Esc => self.close_overlay(),
169 KeyCode::Enter => {
170 let index = self.pickers.table_picker.selected_original()?;
171 let tables = self.pickers.table_choices.take()?;
172 self.close_overlay();
173 if tables.current == Some(index) {
174 return None;
175 }
176 let table = tables.tables.get(index)?.table.clone();
177 return self.switch_table(table);
178 }
179 KeyCode::Up => self.pickers.table_picker.move_up(),
180 KeyCode::Down => self.pickers.table_picker.move_down(),
181 KeyCode::Backspace => self.pickers.table_picker.backspace(),
182 KeyCode::Char(c) => self.pickers.table_picker.filter_key(c, event.modifiers),
183 _ => {}
184 }
185 None
186 }
187
188 pub(crate) fn switch_table(&mut self, table: Option<String>) -> Option<AppEvent> {
192 let (paths, options) = self.source.opened.clone()?;
193 let shown = match &table {
194 Some(name) => crate::formats::members::place(&paths[0], name),
195 None => paths[0].clone(),
196 };
197 let options = OpenOptions {
198 table,
199 view: None,
201 prepared: None,
202 ..options
203 };
204 self.set_loading_phase("Scanning input", 10);
205 self.name_what_is_loading(shown);
206 Some(AppEvent::Open(paths, options))
207 }
208}