From bdcf5ca8949d1ec2687c4591fa63149beba0a5ad Mon Sep 17 00:00:00 2001 From: David Bliss Date: Tue, 26 Mar 2024 08:47:58 +0000 Subject: [PATCH] Basic year view implementation --- Cargo.lock | 1 + Cargo.toml | 1 + src/app.rs | 12 +-- src/main.rs | 1 + src/year_photos.rs | 188 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 src/year_photos.rs diff --git a/Cargo.lock b/Cargo.lock index 9ea11a65..69147a25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1326,6 +1326,7 @@ checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" name = "photo-romantic" version = "0.1.0" dependencies = [ + "chrono", "gettext-rs", "photos_core", "relm4", diff --git a/Cargo.toml b/Cargo.toml index 9db6be7e..4bdb3393 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ gettext-rs = { version = "0.7", features = ["gettext-system"] } tracing = "0.1.37" tracing-subscriber = "0.3" relm4 = { version = "0.8.1", features = ["libadwaita", "gnome_45"] } +chrono = "0.4.35" [dependencies.photos_core] diff --git a/src/app.rs b/src/app.rs index a98bb766..bcb64a03 100644 --- a/src/app.rs +++ b/src/app.rs @@ -8,7 +8,6 @@ use relm4::{ Controller, SimpleComponent, }; -use gtk::glib::prelude::*; use gtk::prelude::{ ApplicationExt, ApplicationWindowExt, GtkWindowExt, OrientableExt, SettingsExt, WidgetExt, }; @@ -16,12 +15,14 @@ use gtk::{gio, glib}; use relm4::adw::prelude::AdwApplicationWindowExt; use crate::all_photos::AllPhotos; +use crate::year_photos::YearPhotos; use crate::config::{APP_ID, PROFILE}; use crate::modals::about::AboutDialog; pub(super) struct App { about_dialog: Controller, all_photos: Controller, + year_photos: Controller, } #[derive(Debug)] @@ -105,11 +106,7 @@ impl SimpleComponent for App { #[name(stack)] adw::ViewStack { - add_titled_with_icon[None, "Year", "year-symbolic"] = >k::Box { - gtk::Label { - set_label: "Hello", - } - }, + add_titled_with_icon[None, "Year", "year-symbolic"] = model.year_photos.widget(), add_titled_with_icon[None, "Month", "month-symbolic"] = >k::Box { gtk::Label { @@ -141,10 +138,13 @@ impl SimpleComponent for App { .detach(); let all_photos = AllPhotos::builder().launch(()).detach(); + let year_photos = YearPhotos::builder().launch(()).detach(); + let model = Self { about_dialog, all_photos, + year_photos, }; let widgets = view_output!(); diff --git a/src/main.rs b/src/main.rs index fe5aea96..f1d33cc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,6 +5,7 @@ #[rustfmt::skip] mod config; mod all_photos; +mod year_photos; mod app; mod modals; diff --git a/src/year_photos.rs b/src/year_photos.rs new file mode 100644 index 00000000..e95e2e27 --- /dev/null +++ b/src/year_photos.rs @@ -0,0 +1,188 @@ +// SPDX-FileCopyrightText: © 2024 David Bliss +// +// SPDX-License-Identifier: GPL-3.0-or-later + +use gtk::glib; +use gtk::prelude::{BoxExt, OrientableExt}; +use photos_core; +use chrono::Datelike; + +use relm4::gtk; +use relm4::gtk::prelude::WidgetExt; +use relm4::typed_view::grid::{RelmGridItem, TypedGridView}; +use relm4::*; +use std::cell::RefCell; +use std::path; +use std::rc::Rc; +use relm4::gtk::prelude::FrameExt; + +#[derive(Debug)] +pub struct PicturePreview { + controller: Rc>, + picture: photos_core::repo::Picture, + year: u32, +} + +pub struct Widgets { + picture: gtk::Picture, + frame: gtk::Frame, +} + +impl RelmGridItem for PicturePreview { + type Root = gtk::Box; + type Widgets = Widgets; + + fn setup(_item: >k::ListItem) -> (gtk::Box, Widgets) { + relm4::view! { + my_box = gtk::Box { + set_orientation: gtk::Orientation::Vertical, + set_margin_all: 1, + + #[name = "frame"] + gtk::Frame { + #[name = "picture"] + gtk::Picture { + set_can_shrink: true, + set_valign: gtk::Align::Center, + set_width_request: 200, + set_height_request: 200, + } + } + } + } + + let widgets = Widgets { picture, frame }; + + (my_box, widgets) + } + + fn bind(&mut self, widgets: &mut Self::Widgets, _root: &mut Self::Root) { + widgets.frame.set_label(Some(format!("{}", self.year).as_str())); + + // compute preview image if it is absent + if self.picture.square_preview_path.is_none() { + let mut controller = self.controller.borrow_mut(); + match controller.add_preview(&mut self.picture) { + Ok(_) => {} + Err(e) => { + println!( + "Failed computing preview for {:?} with {:?}", + self.picture.path, e + ); + } + } + } + + if widgets.picture.file().is_none() { + widgets + .picture + .set_filename(self.picture.square_preview_path.clone()); + } + } +} + +pub struct YearPhotos { + // controller: photos_core::Controller, + pictures_grid_view: TypedGridView, +} + +#[relm4::component(pub)] +impl SimpleComponent for YearPhotos { + type Init = (); + type Input = (); + type Output = (); + + view! { + gtk::Box { + set_orientation: gtk::Orientation::Vertical, + set_spacing: 0, + set_margin_all: 0, + + gtk::ScrolledWindow { + //set_propagate_natural_height: true, + //set_has_frame: true, + set_vexpand: true, + + #[local_ref] + pictures_box -> gtk::GridView { + set_orientation: gtk::Orientation::Vertical, + //set_max_columns: 3, + }, + }, + } + } + + fn init( + _init: Self::Init, + root: Self::Root, + _sender: ComponentSender, + ) -> ComponentParts { + let data_dir = glib::user_data_dir().join("photo-romantic"); + let _ = std::fs::create_dir_all(&data_dir); + + let cache_dir = glib::user_cache_dir().join("photo-romantic"); + let _ = std::fs::create_dir_all(&cache_dir); + + let pic_base_dir = path::Path::new("/var/home/david/Pictures"); + let repo = { + let db_path = data_dir.join("pictures.sqlite"); + photos_core::Repository::open(&pic_base_dir, &db_path).unwrap() + }; + + let scan = photos_core::Scanner::build(&pic_base_dir).unwrap(); + + let previewer = { + let preview_base_path = cache_dir.join("previews"); + let _ = std::fs::create_dir_all(&preview_base_path); + photos_core::Previewer::build(&preview_base_path).unwrap() + }; + + let mut controller = photos_core::Controller::new(scan, repo, previewer); + + // Time consuming! + match controller.scan() { + Err(e) => { + println!("Failed scanning: {:?}", e); + } + _ => {} + } + + let controller = Rc::new(RefCell::new(controller)); + + { + //let result = controller.borrow_mut().update_previews(); + //println!("preview result: {:?}", result); + } + + let all_pictures = controller + .borrow_mut() + //.all() + .all_with_previews() + .unwrap() + .into_iter() + .map(|picture| { + let year = picture.order_by_ts.map(|ts| ts.date_naive().year_ce().1).unwrap(); + PicturePreview { + picture, + year, + controller: controller.clone(), + } + }); + + let mut grid_view_wrapper: TypedGridView = + TypedGridView::new(); + + grid_view_wrapper.extend_from_iter(all_pictures.into_iter()); + + let model = YearPhotos { + pictures_grid_view: grid_view_wrapper, + }; + + let pictures_box = &model.pictures_grid_view.view; + + let widgets = view_output!(); + ComponentParts { model, widgets } + } + + fn update(&mut self, _msg: Self::Input, _sender: ComponentSender) {} +}