Wire 4 orphaned dialogs: chattr (C-x e), filter (Alt-f), encoding (Alt-e), connection (Alt-n)

4 dialog modules existed with full implementations and tests but were
completely unreachable — no Cmd variants, no DialogState variants, no
dispatch arms, no keymap bindings. This commit wires them in:

- ChattrDialog: C-x e — file attribute viewer for cursor file
- FilterDialog: Alt-f — persistent panel glob filter
- EncodingDialog: Alt-e — display encoding selector
- ConnectionDialog: Alt-n — network connection (FTP/SFTP/Shell)

All 4 now have: Cmd variants, DialogState variants, dispatch arms,
handle_dialog_key arms, render routing, apply_finished_dialog no-ops.
1022 tests pass, 0 new warnings.
This commit is contained in:
2026-06-19 20:10:26 +03:00
parent 272f098884
commit 477b7efc33
5 changed files with 126 additions and 2 deletions
@@ -664,7 +664,11 @@ impl FileManager {
| Some(DialogState::ScreenList(_))
| Some(DialogState::EditHistory(_))
| Some(DialogState::FilteredView(_))
| Some(DialogState::Compare(_)) => {
| Some(DialogState::Compare(_))
| Some(DialogState::Chattr(_))
| Some(DialogState::PanelFilter(_))
| Some(DialogState::Encoding(_))
| Some(DialogState::Connection(_)) => {
// No-op: those dialogs clear themselves.
}
// The Help dialog also clears itself in `handle_dialog_key`
@@ -1012,5 +1016,9 @@ fn dialog_label(d: &DialogState) -> String {
DialogState::EditHistory(_) => "Directory history".to_string(),
DialogState::FilteredView(_) => "Filtered view".to_string(),
DialogState::Compare(_) => "Compare files".to_string(),
DialogState::Chattr(_) => "File attributes".to_string(),
DialogState::PanelFilter(_) => "Panel filter".to_string(),
DialogState::Encoding(_) => "Display encoding".to_string(),
DialogState::Connection(_) => "Network connection".to_string(),
}
}
@@ -12,7 +12,8 @@ use std::sync::Arc;
use anyhow::Result;
use crate::filemanager::{
compare, config_dialog, edit_history, external_panelize, filtered_view,
chattr_dialog, compare, config_dialog, connection_dialog, edit_history,
encoding_dialog, external_panelize, filter_dialog, filtered_view,
find, format_utils, hotlist, jobs, layout_dialog, link, menubar,
panel_options, quickcd_dialog, screen_list, skin_dialog,
tree, usermenu, vfs_list, Cmd, DialogState, FileManager,
@@ -454,6 +455,32 @@ impl FileManager {
.set_message(if was_qv { "Quick view off" } else { "Quick view on" });
Ok(true)
}
Cmd::Chattr => {
let p = self.active_panel().cursor_path();
self.dialog = Some(DialogState::Chattr(Box::new(
chattr_dialog::ChattrDialog::new(p),
)));
Ok(true)
}
Cmd::PanelFilter => {
let current = self.active_panel().filter().map(|s| s.to_string());
self.dialog = Some(DialogState::PanelFilter(Box::new(
filter_dialog::FilterDialog::new(current),
)));
Ok(true)
}
Cmd::Encoding => {
self.dialog = Some(DialogState::Encoding(Box::default()));
Ok(true)
}
Cmd::Connection => {
self.dialog = Some(DialogState::Connection(Box::new(
connection_dialog::ConnectionDialog::new(
connection_dialog::ConnectionKind::Ftp,
),
)));
Ok(true)
}
}
}
@@ -470,6 +497,7 @@ impl FileManager {
let ch = char::from_u32(key.code);
let cmd = match ch {
Some('d') => Some(Cmd::CompareDirs),
Some('e') => Some(Cmd::Chattr),
Some('j') => Some(Cmd::Jobs),
Some('c') => Some(Cmd::Permission),
Some('o') => Some(Cmd::Owner),
@@ -633,6 +661,59 @@ impl FileManager {
}
consumed = true;
}
Some(DialogState::Chattr(d)) => {
let r = d.handle_key(key);
if matches!(r, chattr_dialog::ChattrResult::Close) {
self.dialog = None;
}
consumed = true;
}
Some(DialogState::PanelFilter(d)) => {
let r = d.handle_key(key);
match r {
filter_dialog::FilterResult::Apply(pat) => {
self.active_panel_mut().set_filter(&pat);
self.dialog = None;
}
filter_dialog::FilterResult::Clear => {
self.active_panel_mut().set_filter("");
self.dialog = None;
}
filter_dialog::FilterResult::Cancel => {
self.dialog = None;
}
filter_dialog::FilterResult::Running => {}
}
consumed = true;
}
Some(DialogState::Encoding(d)) => {
let r = d.handle_key(key);
match r {
encoding_dialog::EncodingResult::Select(enc) => {
self.status.set_message(format!("Display encoding: {enc}"));
self.dialog = None;
}
encoding_dialog::EncodingResult::Cancel => {
self.dialog = None;
}
encoding_dialog::EncodingResult::Running => {}
}
consumed = true;
}
Some(DialogState::Connection(d)) => {
let r = d.handle_key(key);
match r {
connection_dialog::ConnectionResult::Connect(url) => {
self.status.set_message(format!("Connect: {url}"));
self.dialog = None;
}
connection_dialog::ConnectionResult::Cancel => {
self.dialog = None;
}
connection_dialog::ConnectionResult::Running => {}
}
consumed = true;
}
None => return false,
}
// Apply captured outcomes.
@@ -80,6 +80,10 @@ use self::owner::OwnerDialog;
use self::panel::{Panel, SortField};
use self::panel_options::PanelOptionsDialog;
use self::permission::PermissionDialog;
use self::chattr_dialog::ChattrDialog;
use self::filter_dialog::FilterDialog;
use self::encoding_dialog::EncodingDialog;
use self::connection_dialog::ConnectionDialog;
/// Which panel is active.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -268,6 +272,14 @@ pub enum DialogState {
FilteredView(Box<filtered_view::FilteredViewDialog>),
/// File comparison diff viewer.
Compare(Box<compare::CompareDialog>),
/// C-x e — file attributes viewer.
Chattr(Box<ChattrDialog>),
/// Alt-f — persistent panel filter.
PanelFilter(Box<FilterDialog>),
/// Alt-e — display encoding selector.
Encoding(Box<EncodingDialog>),
/// Alt-n — network connection dialog.
Connection(Box<ConnectionDialog>),
}
impl DialogState {
@@ -319,6 +331,10 @@ impl DialogState {
DialogState::EditHistory(_) => false,
DialogState::FilteredView(_) => false,
DialogState::Compare(_) => false,
DialogState::Chattr(_) => false,
DialogState::PanelFilter(_) => false,
DialogState::Encoding(_) => false,
DialogState::Connection(_) => false,
}
}
}
@@ -124,6 +124,10 @@ impl FileManager {
DialogState::EditHistory(d) => d.render(frame, area, &self.theme),
DialogState::FilteredView(d) => d.render(frame, area, &self.theme),
DialogState::Compare(d) => d.render(frame, area, &self.theme),
DialogState::Chattr(d) => d.render(frame, area, &self.theme),
DialogState::PanelFilter(d) => d.render(frame, area, &self.theme),
DialogState::Encoding(d) => d.render(frame, area, &self.theme),
DialogState::Connection(d) => d.render(frame, area, &self.theme),
}
}
}
@@ -171,6 +171,14 @@ pub enum Cmd {
/// (inline preview of the cursor file) and its prior listing.
/// Dispatched via [`crate::filemanager::FileManager`].
PanelQuickView,
/// C-x e — view file attributes (read-only metadata viewer).
Chattr,
/// Alt-f — persistent panel filter (glob pattern).
PanelFilter,
/// Alt-e — change panel display encoding.
Encoding,
/// Alt-n — open a network connection (FTP/SFTP/Shell).
Connection,
}
impl Cmd {
@@ -247,6 +255,10 @@ impl Cmd {
Cmd::FilteredView => "Filtered view",
Cmd::PanelInfo => "Panel info",
Cmd::PanelQuickView => "Panel quick view",
Cmd::Chattr => "File attributes",
Cmd::PanelFilter => "Panel filter",
Cmd::Encoding => "Display encoding",
Cmd::Connection => "Network connection",
}
}
}
@@ -385,6 +397,9 @@ pub fn default_keymap() -> Keymap {
km.bind(Key::alt('l'), Cmd::ListingCycle);
km.bind(Key::alt('g'), Cmd::LayoutDialog);
km.bind(Key::alt('p'), Cmd::PanelOptionsDialog);
km.bind(Key::alt('f'), Cmd::PanelFilter);
km.bind(Key::alt('e'), Cmd::Encoding);
km.bind(Key::alt('n'), Cmd::Connection);
// C-x prefix family: mapped CTRL|ALT+<key>, matching the existing
// C-x c / C-x o / C-x l / C-x s convention above.