Add collapsible watch folders panel in main window

Toggle button in the header bar reveals a bottom panel showing active
watch folders with their linked presets and watching status. The add
button opens Settings for full watch folder configuration.
This commit is contained in:
2026-03-06 17:29:02 +02:00
parent 0460763d42
commit 45247cdac5

View File

@@ -456,10 +456,35 @@ fn build_ui(app: &adw::Application) {
bottom_bar.append(&separator);
bottom_bar.append(&bottom_box);
// Watch folders collapsible panel
let watch_revealer = gtk::Revealer::builder()
.transition_type(gtk::RevealerTransitionType::SlideUp)
.reveal_child(false)
.build();
let watch_panel = build_watch_folder_panel();
watch_revealer.set_child(Some(&watch_panel));
// Watch folder toggle button in header
let watch_button = gtk::ToggleButton::builder()
.icon_name("folder-visiting-symbolic")
.tooltip_text("Watch Folders")
.build();
watch_button.add_css_class("flat");
header.pack_start(&watch_button);
{
let revealer = watch_revealer.clone();
watch_button.connect_toggled(move |btn| {
revealer.set_reveal_child(btn.is_active());
});
}
// Main content layout
let content_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
content_box.append(step_indicator.widget());
content_box.append(&nav_view);
content_box.append(&watch_revealer);
// Queue side panel
let (queue_panel, queue_list_box) = build_queue_panel(&app_state);
@@ -3048,6 +3073,112 @@ fn format_duration(ms: u64) -> String {
}
}
fn build_watch_folder_panel() -> gtk::Box {
let panel = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.spacing(0)
.build();
panel.append(&gtk::Separator::new(gtk::Orientation::Horizontal));
let inner = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)
.spacing(8)
.margin_top(8)
.margin_bottom(8)
.margin_start(12)
.margin_end(12)
.build();
let header_box = gtk::Box::builder()
.orientation(gtk::Orientation::Horizontal)
.spacing(8)
.build();
let header_label = gtk::Label::builder()
.label("Watch Folders")
.css_classes(["heading"])
.hexpand(true)
.halign(gtk::Align::Start)
.build();
header_box.append(&header_label);
// Quick add button
let add_btn = gtk::Button::builder()
.icon_name("list-add-symbolic")
.tooltip_text("Add watch folder")
.build();
add_btn.add_css_class("flat");
header_box.append(&add_btn);
inner.append(&header_box);
// List of active watch folders
let list_box = gtk::ListBox::builder()
.selection_mode(gtk::SelectionMode::None)
.css_classes(["boxed-list"])
.build();
let empty_label = gtk::Label::builder()
.label("No watch folders active. Add one in Settings or click +.")
.css_classes(["dim-label", "caption"])
.halign(gtk::Align::Center)
.wrap(true)
.margin_top(4)
.margin_bottom(4)
.build();
// Populate from config
let config_store = pixstrip_core::storage::ConfigStore::new();
let config = config_store.load().unwrap_or_default();
let has_folders = !config.watch_folders.is_empty();
for folder in &config.watch_folders {
if !folder.active {
continue;
}
let display_name = folder.path.file_name()
.and_then(|n| n.to_str())
.unwrap_or_else(|| folder.path.to_str().unwrap_or("Unknown"));
let row = adw::ActionRow::builder()
.title(display_name)
.subtitle(&folder.preset_name)
.build();
row.add_prefix(&gtk::Image::from_icon_name("folder-visiting-symbolic"));
// Status indicator
let status = gtk::Label::builder()
.label("Watching")
.css_classes(["caption", "accent"])
.valign(gtk::Align::Center)
.build();
row.add_suffix(&status);
list_box.append(&row);
}
empty_label.set_visible(!has_folders || config.watch_folders.iter().all(|f| !f.active));
list_box.set_visible(has_folders && config.watch_folders.iter().any(|f| f.active));
inner.append(&list_box);
inner.append(&empty_label);
// Wire add button to open Settings > Watch Folders
{
let inner_ref = inner.clone();
add_btn.connect_clicked(move |btn| {
if let Some(root) = btn.root() {
root.activate_action("win.show-settings", None).ok();
}
let _ = &inner_ref;
});
}
panel.append(&inner);
panel
}
fn build_queue_panel(_state: &AppState) -> (gtk::Box, gtk::ListBox) {
let panel = gtk::Box::builder()
.orientation(gtk::Orientation::Vertical)