Add native file dialogs for workspace and export paths

A 📂 button beside each path field opens a native picker: a folder
chooser for the workspace and an .odt save dialog for the export (which
appends the extension if omitted). Uses rfd with the XDG Desktop Portal
backend, so there is no GTK build dependency and no new shared library
at runtime.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
landon
2026-07-26 14:17:24 -05:00
parent 7008463ebf
commit 7271e5e2ef
5 changed files with 339 additions and 42 deletions
+52 -2
View File
@@ -340,6 +340,50 @@ impl App {
}
}
/// Open a native folder picker to choose the workspace directory.
fn browse_workspace(&mut self) {
let start = PathBuf::from(self.workspace_input.trim());
let mut dialog = rfd::FileDialog::new().set_title("Choose workspace folder");
if start.is_dir() {
dialog = dialog.set_directory(&start);
}
if let Some(path) = dialog.pick_folder() {
self.save_current();
self.workspace_input = path.display().to_string();
self.config.workspace = path;
self.config.save();
self.open_workspace();
}
}
/// Open a native save dialog to choose the export `.odt` path.
fn browse_export(&mut self) {
let current = PathBuf::from(self.export_input.trim());
let mut dialog = rfd::FileDialog::new()
.set_title("Choose export file")
.add_filter("OpenDocument Text", &["odt"]);
if let Some(parent) = current.parent().filter(|p| p.is_dir()) {
dialog = dialog.set_directory(parent);
}
let name = current
.file_name()
.and_then(|s| s.to_str())
.unwrap_or("manuscript.odt");
if let Some(mut path) = dialog.set_file_name(name).save_file() {
// Ensure the chosen path ends in .odt even if the user omitted it.
let has_odt = path
.extension()
.and_then(|e| e.to_str())
.is_some_and(|e| e.eq_ignore_ascii_case("odt"));
if !has_odt {
path.set_extension("odt");
}
self.export_input = path.display().to_string();
self.config.export_path = path;
self.config.save();
}
}
fn reorder(&mut self, from: usize, mut to: usize) {
if from >= self.files.len() || from == to {
return;
@@ -370,8 +414,11 @@ impl App {
ui.label("Workspace:");
let resp = ui.add(
egui::TextEdit::singleline(&mut self.workspace_input)
.desired_width(360.0),
.desired_width(300.0),
);
if ui.button("📂").on_hover_text("Browse for workspace folder").clicked() {
self.browse_workspace();
}
if ui.button("Open").clicked()
|| (resp.lost_focus() && ui.input(|i| i.key_pressed(egui::Key::Enter)))
{
@@ -396,8 +443,11 @@ impl App {
ui.horizontal(|ui| {
ui.label("Export:");
ui.add(
egui::TextEdit::singleline(&mut self.export_input).desired_width(360.0),
egui::TextEdit::singleline(&mut self.export_input).desired_width(300.0),
);
if ui.button("📂").on_hover_text("Browse for export .odt file").clicked() {
self.browse_export();
}
if ui.button("Export ODT").clicked() {
self.export_odt();
}