Show albums on home screen

This commit is contained in:
Elias Projahn 2024-06-05 13:26:47 +02:00
parent 3ab0332475
commit 38613c0063
7 changed files with 172 additions and 3 deletions

25
data/ui/album_tile.blp Normal file
View file

@ -0,0 +1,25 @@
using Gtk 4.0;
using Adw 1;
template $MusicusAlbumTile: Gtk.FlowBoxChild {
styles [
"card",
"activatable",
"tile"
]
Gtk.Box {
valign: center;
Gtk.Label title_label {
styles [
"title"
]
valign: center;
halign: start;
lines: 1;
ellipsize: end;
}
}
}

View file

@ -127,6 +127,23 @@ template $MusicusHomePage : Adw.NavigationPage {
selection-mode: none;
child-activated => $recording_selected() swapped;
}
Gtk.Label {
styles ["heading"]
visible: bind albums_flow_box.visible;
halign: start;
label: _("Albums");
}
Gtk.FlowBox albums_flow_box {
margin-top: 12;
margin-bottom: 24;
column-spacing: 12;
row-spacing: 12;
homogeneous: true;
selection-mode: none;
child-activated => $album_selected() swapped;
}
}
}
};

56
src/album_tile.rs Normal file
View file

@ -0,0 +1,56 @@
use gtk::{glib, subclass::prelude::*};
use std::cell::OnceCell;
use crate::db::models::Album;
mod imp {
use super::*;
#[derive(Debug, Default, gtk::CompositeTemplate)]
#[template(file = "data/ui/album_tile.blp")]
pub struct MusicusAlbumTile {
pub album: OnceCell<Album>,
#[template_child]
pub title_label: TemplateChild<gtk::Label>,
}
#[glib::object_subclass]
impl ObjectSubclass for MusicusAlbumTile {
const NAME: &'static str = "MusicusAlbumTile";
type Type = super::MusicusAlbumTile;
type ParentType = gtk::FlowBoxChild;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
}
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
obj.init_template();
}
}
impl ObjectImpl for MusicusAlbumTile {}
impl WidgetImpl for MusicusAlbumTile {}
impl FlowBoxChildImpl for MusicusAlbumTile {}
}
glib::wrapper! {
pub struct MusicusAlbumTile(ObjectSubclass<imp::MusicusAlbumTile>)
@extends gtk::Widget, gtk::FlowBoxChild;
}
impl MusicusAlbumTile {
pub fn new(album: &Album) -> Self {
let obj: Self = glib::Object::new();
obj.imp().title_label.set_label(&album.name.get());
obj.imp().album.set(album.clone()).unwrap();
obj
}
pub fn album(&self) -> &Album {
self.imp().album.get().unwrap()
}
}

View file

@ -9,7 +9,7 @@ use diesel::prelude::*;
use super::{schema::*, tables, TranslatedString};
// Re-exports for tables that don't need additional information.
pub use tables::{Instrument, Person, Role};
pub use tables::{Album, Instrument, Person, Role};
#[derive(Clone, Debug)]
pub struct Work {
@ -371,3 +371,10 @@ impl Track {
})
}
}
impl Eq for Album {}
impl PartialEq for Album {
fn eq(&self, other: &Self) -> bool {
self.album_id == other.album_id
}
}

View file

@ -1,4 +1,5 @@
use crate::{
album_tile::MusicusAlbumTile,
db::models::*,
library::{LibraryQuery, MusicusLibrary},
player::MusicusPlayer,
@ -8,11 +9,13 @@ use crate::{
search_tag::Tag,
tag_tile::MusicusTagTile,
};
use adw::subclass::{navigation_page::NavigationPageImpl, prelude::*};
use gtk::{
glib::{self, clone, Properties},
prelude::*,
};
use std::cell::{OnceCell, RefCell};
mod imp {
@ -33,6 +36,7 @@ mod imp {
pub ensembles: RefCell<Vec<Ensemble>>,
pub works: RefCell<Vec<Work>>,
pub recordings: RefCell<Vec<Recording>>,
pub albums: RefCell<Vec<Album>>,
#[template_child]
pub search_entry: TemplateChild<MusicusSearchEntry>,
@ -49,6 +53,8 @@ mod imp {
#[template_child]
pub recordings_flow_box: TemplateChild<gtk::FlowBox>,
#[template_child]
pub albums_flow_box: TemplateChild<gtk::FlowBox>,
#[template_child]
pub play_button: TemplateChild<gtk::Button>,
}
@ -120,13 +126,14 @@ impl MusicusHomePage {
fn select(&self, search_entry: &MusicusSearchEntry) {
let imp = self.imp();
let (composer, performer, ensemble, work, recording) = {
let (composer, performer, ensemble, work, recording, album) = {
(
imp.composers.borrow().first().cloned(),
imp.performers.borrow().first().cloned(),
imp.ensembles.borrow().first().cloned(),
imp.works.borrow().first().cloned(),
imp.recordings.borrow().first().cloned(),
imp.albums.borrow().first().cloned(),
)
};
@ -140,6 +147,8 @@ impl MusicusHomePage {
search_entry.add_tag(Tag::Work(work));
} else if let Some(recording) = recording {
self.play_recording(&recording);
} else if let Some(album) = album {
self.show_album(&album);
}
}
@ -159,6 +168,11 @@ impl MusicusHomePage {
);
}
#[template_callback]
fn album_selected(&self, tile: &gtk::FlowBoxChild, _: &gtk::FlowBox) {
self.show_album(tile.downcast_ref::<MusicusAlbumTile>().unwrap().album());
}
fn play_recording(&self, recording: &Recording) {
let tracks = &recording.tracks;
@ -227,6 +241,10 @@ impl MusicusHomePage {
self.player().append(items);
}
fn show_album(&self, _album: &Album) {
todo!("Show album");
}
fn query(&self, query: &LibraryQuery) {
let imp = self.imp();
let results = self.library().query(query).unwrap();
@ -237,6 +255,7 @@ impl MusicusHomePage {
&imp.ensembles_flow_box,
&imp.works_flow_box,
&imp.recordings_flow_box,
&imp.albums_flow_box,
] {
while let Some(widget) = flowbox.first_child() {
flowbox.remove(&widget);
@ -257,6 +276,7 @@ impl MusicusHomePage {
imp.works_flow_box.set_visible(!results.works.is_empty());
imp.recordings_flow_box
.set_visible(!results.recordings.is_empty());
imp.albums_flow_box.set_visible(!results.albums.is_empty());
for composer in &results.composers {
imp.composers_flow_box
@ -283,11 +303,16 @@ impl MusicusHomePage {
.append(&MusicusRecordingTile::new(recording));
}
for album in &results.albums {
imp.albums_flow_box.append(&MusicusAlbumTile::new(album));
}
imp.composers.replace(results.composers);
imp.performers.replace(results.performers);
imp.ensembles.replace(results.ensembles);
imp.works.replace(results.works);
imp.recordings.replace(results.recordings);
imp.albums.replace(results.albums);
}
}
}

View file

@ -109,11 +109,17 @@ impl MusicusLibrary {
.map(|w| Work::from_table(w, connection))
.collect::<Result<Vec<Work>>>()?;
let albums: Vec<Album> = albums::table
.filter(albums::name.like(&search))
.limit(9)
.load(connection)?;
LibraryResults {
composers,
performers,
ensembles,
works,
albums,
..Default::default()
}
}
@ -219,9 +225,24 @@ impl MusicusLibrary {
.map(|r| Recording::from_table(r, &&self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
let albums = albums::table
.inner_join(
album_recordings::table
.inner_join(recordings::table.inner_join(recording_ensembles::table)),
)
.filter(
recording_ensembles::ensemble_id
.eq(&ensemble.ensemble_id)
.and(albums::name.like(&search)),
)
.select(albums::all_columns)
.distinct()
.load(connection)?;
LibraryResults {
composers,
recordings,
albums,
..Default::default()
}
}
@ -265,9 +286,24 @@ impl MusicusLibrary {
.map(|r| Recording::from_table(r, &self.folder(), connection))
.collect::<Result<Vec<Recording>>>()?;
let albums = albums::table
.inner_join(
album_recordings::table
.inner_join(recordings::table.inner_join(recording_persons::table)),
)
.filter(
recording_persons::person_id
.eq(&performer.person_id)
.and(albums::name.like(&search)),
)
.select(albums::all_columns)
.distinct()
.load(connection)?;
LibraryResults {
composers,
recordings,
albums,
..Default::default()
}
}
@ -426,6 +462,7 @@ pub struct LibraryResults {
pub ensembles: Vec<Ensemble>,
pub works: Vec<Work>,
pub recordings: Vec<Recording>,
pub albums: Vec<Album>,
}
impl LibraryResults {
@ -435,5 +472,6 @@ impl LibraryResults {
&& self.ensembles.is_empty()
&& self.works.is_empty()
&& self.recordings.is_empty()
&& self.albums.is_empty()
}
}

View file

@ -1,10 +1,11 @@
mod album_tile;
mod application;
mod config;
mod db;
mod editor;
mod home_page;
mod library_manager;
mod library;
mod library_manager;
mod player;
mod player_bar;
mod playlist_item;