use crate::app::form::Form;
use crate::widgets::text_input::{TextInput, one_line};
use crate::widgets::ui::PickerState;
use crate::{App, AppEvent, InputMode, InputType, Overlay};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum PasteTarget {
Home,
Field,
Nowhere,
}
pub(crate) enum FocusedText<'a> {
Input(&'a mut TextInput),
Picker(&'a mut PickerState),
Filter(&'a mut String),
}
impl FocusedText<'_> {
fn insert(self, text: &str) -> bool {
match self {
FocusedText::Input(input) => input.insert_text(text),
FocusedText::Picker(picker) => {
let before = picker.filter.len();
picker.type_text(text);
picker.filter.len() != before
}
FocusedText::Filter(filter) => {
let text = one_line(text);
filter.push_str(&text);
!text.is_empty()
}
}
}
}
impl App {
pub(crate) fn paste_target(&self) -> PasteTarget {
if self.help.is_open()
|| self.confirmation_modal.active
|| self.error_modal.active
|| self.context_menu.is_some()
{
return PasteTarget::Nowhere;
}
if self.input_mode == InputMode::Home && self.overlay == Overlay::None {
if self.info.documentation.is_open() {
return PasteTarget::Nowhere;
}
return PasteTarget::Home;
}
if self.text_field_focused() {
return PasteTarget::Field;
}
PasteTarget::Nowhere
}
pub(crate) fn focused_text_mut(&mut self) -> Option<FocusedText<'_>> {
use crate::app::modals::filter_modal::FilterEditStep;
use FocusedText::{Filter, Input, Picker};
if !self.text_field_focused() {
return None;
}
match &mut self.overlay {
Overlay::None => match self.prompt.input_type {
Some(InputType::Query) => {
self.sync_query_focus();
Some(Input(self.query_input_mut()))
}
Some(InputType::Find) => Some(Input(&mut self.prompt.find.input)),
None => None,
},
Overlay::Analysis => {
let modal = &mut self.analysis_modal;
if modal.sample_scope_typing() {
let form = modal.sample_form.as_mut()?;
let field = form.field;
form.input_mut(field).map(Input)
} else if modal.quality_expected_typing() {
modal.quality.expected_form.as_mut()?.input_mut().map(Input)
} else if modal.intent_typing() {
modal.quality.intent_form.as_mut()?.input_mut().map(Input)
} else if modal.export_typing() {
Some(Input(&mut modal.quality.export.as_mut()?.path))
} else {
None
}
}
Overlay::View => self.view_modal.focused_input_mut().map(Input),
Overlay::Export { .. } => match self.export_modal.focus {
crate::ExportFocus::PathInput => Some(Input(&mut self.export_modal.path_input)),
crate::ExportFocus::CsvDelimiter => {
Some(Input(&mut self.export_modal.csv_delimiter_input))
}
_ => None,
},
Overlay::Copy => self.copy_modal.shown_picker().map(|(p, _)| Picker(p)),
Overlay::Inspect => Some(Filter(&mut self.inspector_modal.filter)),
Overlay::GoToColumn => Some(Picker(&mut self.pickers.go_to_column)),
Overlay::PickFormat => Some(Picker(&mut self.pickers.format_picker)),
Overlay::PickTable => Some(Picker(&mut self.pickers.table_picker)),
Overlay::Retype { .. } => Some(Picker(&mut self.column_forms.retype.as_mut()?.picker)),
Overlay::Combine { .. } => {
let modal = self.column_forms.combine.as_mut()?;
if modal.picker.is_some() {
modal.shown_picker().map(|(p, _)| Picker(p))
} else {
Some(Input(&mut modal.name))
}
}
Overlay::Sample => {
let form = self.sample.form.as_mut()?;
let field = form.field;
form.input_mut(field).map(Input)
}
Overlay::SortFilter => {
let modal = &mut self.sort_filter_modal;
if let Some(editor) = modal.filter.editor.as_mut() {
Some(match editor.step {
FilterEditStep::Column => Picker(&mut editor.column),
FilterEditStep::Operator => Picker(&mut editor.operator),
FilterEditStep::Value => Input(&mut editor.value),
})
} else if let Some(picker) = modal.sort_picker.as_mut() {
Some(Picker(picker))
} else {
Some(Input(&mut modal.sort.filter_input))
}
}
Overlay::PivotMelt => {
let modal = &mut self.pivot_melt_modal;
if modal.picker.is_some() {
modal.shown_picker().map(|(p, _)| Picker(p))
} else {
modal.focused_text_input_mut().map(Input)
}
}
Overlay::ChartExport => {
let modal = &mut self.chart.export_modal;
let focus = modal.focus;
modal.input_mut(focus).map(Input)
}
Overlay::Chart => self.chart.modal.shown_picker().map(|(p, _)| Picker(p)),
Overlay::Hex => {
let view = self.hex.view.as_mut()?;
match view.picker.as_mut() {
Some(picker) => Some(Picker(picker)),
None => Some(Input(&mut view.input)),
}
}
Overlay::Info | Overlay::ValueCounts => None,
}
}
pub(crate) fn paste(&mut self, text: &str) -> Option<AppEvent> {
match self.paste_target() {
PasteTarget::Home => self.paste_at_home(one_line(text).trim()),
PasteTarget::Field => {
let text = self.as_the_field_takes(text);
if let Some(focused) = self.focused_text_mut()
&& focused.insert(&text)
{
self.typed_into_focused();
}
}
PasteTarget::Nowhere => {}
}
None
}
fn as_the_field_takes(&self, text: &str) -> String {
use crate::chart::chart_export_modal::ChartExportFocus;
let digits = self.overlay == Overlay::ChartExport
&& matches!(
self.chart.export_modal.focus,
ChartExportFocus::WidthInput | ChartExportFocus::HeightInput
);
if digits {
text.chars().filter(char::is_ascii_digit).collect()
} else {
text.to_string()
}
}
fn typed_into_focused(&mut self) {
match &self.overlay {
Overlay::None => match self.prompt.input_type {
Some(InputType::Query) => {
self.prompt.query_text_restored = false;
self.prompt.query_run_error = None;
}
Some(InputType::Find) => {
self.prompt.find.error = None;
self.refresh_live_matches();
}
None => {}
},
Overlay::Analysis => {
let quality = &mut self.analysis_modal.quality;
if let Some(form) = quality.expected_form.as_mut() {
form.error = None;
}
if let Some(form) = quality.intent_form.as_mut() {
form.error = None;
}
if let Some(form) = quality.export.as_mut() {
form.error = None;
}
if let Some(form) = self.analysis_modal.sample_form.as_mut() {
form.edited();
}
}
Overlay::View => {
if self.view_modal.form_focus == crate::widgets::view_modal::FormFocus::Name {
self.view_modal.name_error = None;
}
}
Overlay::Export { .. } => {
if self.export_modal.focus == crate::ExportFocus::PathInput {
self.export_modal.sync_format_to_path();
self.export_modal.path_error = None;
}
}
Overlay::Inspect => self.refresh_inspector_list(),
Overlay::Combine { .. } => {
if let Some(modal) = self.column_forms.combine.as_mut() {
modal.problem = None;
}
}
Overlay::Sample => {
if let Some(form) = self.sample.form.as_mut() {
form.edited();
}
}
Overlay::SortFilter => {
let modal = &mut self.sort_filter_modal;
if modal.filter.editor.is_none() && modal.sort_picker.is_none() {
modal.sort.table_state.select(Some(0));
}
}
Overlay::PivotMelt => self.pivot_melt_modal.attention = false,
Overlay::ChartExport => {
use crate::chart::chart_export_modal::ChartExportFocus;
let modal = &mut self.chart.export_modal;
match modal.focus {
ChartExportFocus::PathInput => modal.error = None,
ChartExportFocus::WidthInput | ChartExportFocus::HeightInput => {
modal.size_typed()
}
_ => {}
}
}
Overlay::Hex => {
if let Some(view) = self.hex.view.as_mut() {
view.prompt_error = None;
}
}
Overlay::Copy
| Overlay::GoToColumn
| Overlay::PickFormat
| Overlay::PickTable
| Overlay::Retype { .. }
| Overlay::Chart
| Overlay::Info
| Overlay::ValueCounts => {}
}
}
fn paste_at_home(&mut self, text: &str) {
if text.is_empty() {
return;
}
self.home.status = None;
self.home.apply_new_measurements();
if !self.home.path_input_active && std::mem::take(&mut self.home.filter_selected) {
self.home.filter.clear();
self.home.sync_search_section();
self.home.select_first_entry();
}
if !self.home.path_input_active && self.home.filter.is_empty() && text.starts_with('~') {
self.home.path_input_active = true;
self.home.path_input.clear();
self.home.path_listing = None;
self.home.path_pick = None;
}
if self.home.path_input_active {
self.home.path_input.push_str(text);
self.list_the_typed_directory();
self.home.pick_first_path();
return;
}
self.home.filter.push_str(text);
self.spawn_home_search();
self.home.sync_search_section();
self.home.select_first_entry();
#[cfg(feature = "cloud")]
self.narrow_cloud_listing();
}
}