1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// (C) 2025 - Enzo Lombardi
//! Change Directory Dialog - specialized dialog for directory selection
//!
//! Matches Borland: TChDirDialog
//!
//! A simplified wrapper around FileDialog that only shows directories.
//! Provides a cleaner interface for changing the working directory.
use crate::core::geometry::Rect;
use crate::core::event::Event;
use crate::core::command::{CM_OK, CommandId};
use crate::terminal::Terminal;
use super::file_dialog::FileDialog;
use super::View;
use std::path::PathBuf;
/// Change Directory Dialog
/// Matches Borland: TChDirDialog
///
/// This is a thin wrapper around FileDialog configured for directory selection only.
/// In Rust, we leverage the existing FileDialog infrastructure rather than
/// duplicating code, which is more maintainable and idiomatic.
pub struct ChDirDialog {
file_dialog: FileDialog,
selected_directory: Option<PathBuf>,
}
impl ChDirDialog {
/// Create a new change directory dialog
///
/// # Arguments
/// * `bounds` - The dialog bounds
/// * `title` - The dialog title
/// * `initial_dir` - Initial directory to show (defaults to current directory)
pub fn new(bounds: Rect, title: &str, initial_dir: Option<PathBuf>) -> Self {
// Use FileDialog with "*" wildcard to show all directories
// User navigates by selecting directories (they're shown as [dirname])
let file_dialog = FileDialog::new(bounds, title, "*", initial_dir);
Self {
file_dialog,
selected_directory: None,
}
}
/// Execute the dialog modally
///
/// Returns the selected directory if OK was pressed, None if cancelled
pub fn execute(&mut self, app: &mut crate::app::Application) -> Option<PathBuf> {
// FileDialog.execute() returns Option<PathBuf> for selected file
// For directory selection, we ignore the return value and use get_current_directory
let _file_selection = self.file_dialog.execute(app);
// Check end state to see if OK or Cancel was pressed
let end_state = self.file_dialog.get_end_state();
if end_state == CM_OK {
// Get the current directory from the file dialog
let directory = self.file_dialog.get_current_directory();
self.selected_directory = Some(directory.clone());
Some(directory)
} else {
None
}
}
/// Get the selected directory
///
/// Returns Some(path) if OK was pressed, None if cancelled or not executed
pub fn get_directory(&self) -> Option<PathBuf> {
self.selected_directory.clone()
}
/// Get the end state (command that closed the dialog)
pub fn get_end_state(&self) -> CommandId {
self.file_dialog.get_end_state()
}
}
impl View for ChDirDialog {
fn bounds(&self) -> Rect {
self.file_dialog.bounds()
}
fn set_bounds(&mut self, bounds: Rect) {
self.file_dialog.set_bounds(bounds);
}
fn draw(&mut self, terminal: &mut Terminal) {
self.file_dialog.draw(terminal);
}
fn handle_event(&mut self, event: &mut Event) {
self.file_dialog.handle_event(event);
}
fn can_focus(&self) -> bool {
true
}
fn state(&self) -> crate::core::state::StateFlags {
self.file_dialog.state()
}
fn set_state(&mut self, state: crate::core::state::StateFlags) {
self.file_dialog.set_state(state);
}
}