diff --git a/core/src/scanner.rs b/core/src/scanner.rs index 24d6859b..0af1a597 100644 --- a/core/src/scanner.rs +++ b/core/src/scanner.rs @@ -13,7 +13,7 @@ use std::path::PathBuf; use walkdir::WalkDir; /// A picture on the local file system that has been scanned. -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Picture { /// Full path to picture file. pub path: PathBuf, @@ -37,7 +37,7 @@ impl Picture { } /// Metadata from EXIF tags -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct Exif { pub description: Option, pub created_at: Option>, @@ -56,7 +56,7 @@ impl Exif { } /// Metadata from the file system. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub struct FsMetadata { pub created_at: Option>, pub modified_at: Option>, diff --git a/src/app/components/one_photo.rs b/src/app/components/one_photo.rs index c5d15f06..16de86c6 100644 --- a/src/app/components/one_photo.rs +++ b/src/app/components/one_photo.rs @@ -10,6 +10,7 @@ use relm4::*; use std::sync::{Arc, Mutex}; use crate::app::components::photo_info::PhotoInfo; +use crate::app::components::photo_info::PhotoInfoInput; #[derive(Debug)] pub enum OnePhotoInput { @@ -82,7 +83,8 @@ impl SimpleComponent for OnePhoto { println!("Showing photo for {}", picture_id); let result = self.repo.lock().unwrap().get(picture_id); if let Ok(Some(pic)) = result { - self.picture.set_filename(Some(pic.path)); + self.picture.set_filename(Some(pic.path.clone())); + self.photo_info.emit(PhotoInfoInput::ShowInfo(pic.path)); } else { println!("Failed loading {}: {:?}", picture_id, result); } diff --git a/src/app/components/photo_info.rs b/src/app/components/photo_info.rs index f8624d18..04821046 100644 --- a/src/app/components/photo_info.rs +++ b/src/app/components/photo_info.rs @@ -2,25 +2,36 @@ // // SPDX-License-Identifier: GPL-3.0-or-later +/// Properties view for a photo. +/// Deeply inspired by how Loupe displays its property view. + use photos_core::Scanner; +use photos_core::scanner::Picture; use gtk::prelude::OrientableExt; use relm4::gtk; use relm4::*; use relm4::adw::prelude::PreferencesRowExt; use relm4::adw::prelude::ActionRowExt; use relm4::gtk::prelude::WidgetExt; +use std::path::PathBuf; +#[derive(Debug)] +pub enum PhotoInfoInput { + ShowInfo(PathBuf), +} #[derive(Debug)] pub struct PhotoInfo { scanner: Scanner, + + folder: adw::ActionRow, } #[relm4::component(pub)] impl SimpleComponent for PhotoInfo { type Init = Scanner; - type Input = (); + type Input = PhotoInfoInput; type Output = (); view! { @@ -29,16 +40,18 @@ impl SimpleComponent for PhotoInfo { set_margin_all: 10, adw::PreferencesGroup { - adw::ActionRow { - set_title: "Test Title", - set_subtitle: "Subtitle", + + #[local_ref] + folder -> adw::ActionRow { + set_title: "Folder", + //set_subtitle: &model.path, add_css_class: "property", set_subtitle_selectable: true, }, adw::ActionRow { set_title: "Another Title", - } + }, } } } @@ -49,15 +62,63 @@ impl SimpleComponent for PhotoInfo { _sender: ComponentSender, ) -> ComponentParts { - let widgets = view_output!(); + let folder = adw::ActionRow::new(); let model = PhotoInfo { scanner, + folder: folder.clone(), }; + let widgets = view_output!(); + ComponentParts { model, widgets } } - fn update(&mut self, msg: Self::Input, _sender: ComponentSender) {} + fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { + match msg { + PhotoInfoInput::ShowInfo(ref path) => { + println!("Received {:?}", msg); + self.update_pic_info(path); + } + } + } } +/// Value row subtitle when value absent. +const FALLBACK: &str = "–"; + +impl PhotoInfo { + + fn update_pic_info(&mut self, path: &PathBuf) { + let result = self.scanner.scan_one(path); + let Ok(pic) = result else { + println!("Failed scanning picture: {:?}", result); + return; + }; + + Self::update_row(&self.folder, Self::folder_name(path)); + } + + /// Borrowed from Loupe. + /// Updates a row to be visible if it has a value to display, and returns + /// visibility status. + fn update_row(row: &adw::ActionRow, value: Option>) -> bool { + if let Some(value) = value { + row.set_subtitle(value.as_ref()); + row.set_visible(true); + true + } else { + row.set_subtitle(FALLBACK); + row.set_visible(false); + false + } + } + + fn folder_name(path: &PathBuf) -> Option { + path.parent() + .and_then(|p| p.file_name()) + .map(|n| n.to_string_lossy()) + .map(|n| n.to_string()) + } + +}