Split composers and performers

This commit is contained in:
Elias Projahn 2023-10-08 15:11:47 +02:00
parent 84612673c1
commit af57fe020c
5 changed files with 137 additions and 48 deletions

View file

@ -45,12 +45,28 @@ template $MusicusHomePage : Adw.NavigationPage {
Gtk.Label { Gtk.Label {
styles ["heading"] styles ["heading"]
visible: bind persons_flow_box.visible; visible: bind composers_flow_box.visible;
halign: start; halign: start;
label: _("Composers and performers"); label: _("Composers");
} }
Gtk.FlowBox persons_flow_box { Gtk.FlowBox composers_flow_box {
margin-top: 12;
margin-bottom: 24;
column-spacing: 12;
row-spacing: 12;
homogeneous: true;
selection-mode: none;
}
Gtk.Label {
styles ["heading"]
visible: bind performers_flow_box.visible;
halign: start;
label: _("Performers");
}
Gtk.FlowBox performers_flow_box {
margin-top: 12; margin-top: 12;
margin-bottom: 24; margin-bottom: 24;
column-spacing: 12; column-spacing: 12;

View file

@ -25,7 +25,8 @@ mod imp {
#[property(get, construct_only)] #[property(get, construct_only)]
pub player: OnceCell<MusicusPlayer>, pub player: OnceCell<MusicusPlayer>,
pub persons: RefCell<Vec<Person>>, pub composers: RefCell<Vec<Person>>,
pub performers: RefCell<Vec<Person>>,
pub ensembles: RefCell<Vec<Ensemble>>, pub ensembles: RefCell<Vec<Ensemble>>,
pub works: RefCell<Vec<Work>>, pub works: RefCell<Vec<Work>>,
pub recordings: RefCell<Vec<Recording>>, pub recordings: RefCell<Vec<Recording>>,
@ -35,7 +36,9 @@ mod imp {
#[template_child] #[template_child]
pub stack: TemplateChild<gtk::Stack>, pub stack: TemplateChild<gtk::Stack>,
#[template_child] #[template_child]
pub persons_flow_box: TemplateChild<gtk::FlowBox>, pub composers_flow_box: TemplateChild<gtk::FlowBox>,
#[template_child]
pub performers_flow_box: TemplateChild<gtk::FlowBox>,
#[template_child] #[template_child]
pub ensembles_flow_box: TemplateChild<gtk::FlowBox>, pub ensembles_flow_box: TemplateChild<gtk::FlowBox>,
#[template_child] #[template_child]
@ -114,16 +117,19 @@ impl MusicusHomePage {
fn select(&self, search_entry: &MusicusSearchEntry) { fn select(&self, search_entry: &MusicusSearchEntry) {
let imp = self.imp(); let imp = self.imp();
let (person, ensemble, work) = { let (composer, performer, ensemble, work) = {
( (
imp.persons.borrow().first().cloned(), imp.composers.borrow().first().cloned(),
imp.performers.borrow().first().cloned(),
imp.ensembles.borrow().first().cloned(), imp.ensembles.borrow().first().cloned(),
imp.works.borrow().first().cloned(), imp.works.borrow().first().cloned(),
) )
}; };
if let Some(person) = person { if let Some(person) = composer {
search_entry.add_tag(Tag::Person(person)); search_entry.add_tag(Tag::Composer(person));
} else if let Some(person) = performer {
search_entry.add_tag(Tag::Performer(person));
} else if let Some(ensemble) = ensemble { } else if let Some(ensemble) = ensemble {
search_entry.add_tag(Tag::Ensemble(ensemble)); search_entry.add_tag(Tag::Ensemble(ensemble));
} else if let Some(work) = work { } else if let Some(work) = work {
@ -136,7 +142,8 @@ impl MusicusHomePage {
let results = self.library().query(query); let results = self.library().query(query);
for flowbox in [ for flowbox in [
&imp.persons_flow_box, &imp.composers_flow_box,
&imp.performers_flow_box,
&imp.ensembles_flow_box, &imp.ensembles_flow_box,
&imp.works_flow_box, &imp.works_flow_box,
&imp.recordings_flow_box, &imp.recordings_flow_box,
@ -151,17 +158,24 @@ impl MusicusHomePage {
} else { } else {
imp.stack.set_visible_child_name("results"); imp.stack.set_visible_child_name("results");
imp.persons_flow_box imp.composers_flow_box
.set_visible(!results.persons.is_empty()); .set_visible(!results.composers.is_empty());
imp.performers_flow_box
.set_visible(!results.performers.is_empty());
imp.ensembles_flow_box imp.ensembles_flow_box
.set_visible(!results.ensembles.is_empty()); .set_visible(!results.ensembles.is_empty());
imp.works_flow_box.set_visible(!results.works.is_empty()); imp.works_flow_box.set_visible(!results.works.is_empty());
imp.recordings_flow_box imp.recordings_flow_box
.set_visible(!results.recordings.is_empty()); .set_visible(!results.recordings.is_empty());
for person in &results.persons { for composer in &results.composers {
imp.persons_flow_box imp.composers_flow_box
.append(&MusicusTile::with_title(&person.name_fl())); .append(&MusicusTile::with_title(&composer.name_fl()));
}
for performer in &results.performers {
imp.performers_flow_box
.append(&MusicusTile::with_title(&performer.name_fl()));
} }
for ensemble in &results.ensembles { for ensemble in &results.ensembles {
@ -183,7 +197,8 @@ impl MusicusHomePage {
)); ));
} }
imp.persons.replace(results.persons); imp.composers.replace(results.composers);
imp.performers.replace(results.performers);
imp.ensembles.replace(results.ensembles); imp.ensembles.replace(results.ensembles);
imp.works.replace(results.works); imp.works.replace(results.works);
imp.recordings.replace(results.recordings); imp.recordings.replace(results.recordings);

View file

@ -50,13 +50,22 @@ impl MusicusLibrary {
match query { match query {
LibraryQuery { LibraryQuery {
person: None, composer: None,
performer: None,
ensemble: None, ensemble: None,
work: None, work: None,
.. ..
} => { } => {
let persons = self.con() let composers = self.con()
.prepare("SELECT id, first_name, last_name FROM persons WHERE first_name LIKE ?1 OR last_name LIKE ?1 LIMIT 9") .prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN works ON works.composer = persons.id WHERE persons.first_name LIKE ?1 OR persons.last_name LIKE ?1 LIMIT 9")
.unwrap()
.query_map([&search], Person::from_row)
.unwrap()
.collect::<rusqlite::Result<Vec<Person>>>()
.unwrap();
let performers = self.con()
.prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN performances ON performances.person = persons.id WHERE persons.first_name LIKE ?1 OR persons.last_name LIKE ?1 LIMIT 9")
.unwrap() .unwrap()
.query_map([&search], Person::from_row) .query_map([&search], Person::from_row)
.unwrap() .unwrap()
@ -82,22 +91,24 @@ impl MusicusLibrary {
.unwrap(); .unwrap();
LibraryResults { LibraryResults {
persons, composers,
performers,
ensembles, ensembles,
works, works,
..Default::default() ..Default::default()
} }
} }
LibraryQuery { LibraryQuery {
person: Some(person), composer: Some(composer),
performer: None,
ensemble: None, ensemble: None,
work: None, work: None,
.. ..
} => { } => {
let persons = self.con() let performers = self.con()
.prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN performances ON performances.person = persons.id INNER JOIN recordings ON recordings.id = performances.recording INNER JOIN works ON works.id = recordings.work WHERE works.composer IS ?1 AND (persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9") .prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN performances ON performances.person = persons.id INNER JOIN recordings ON recordings.id = performances.recording INNER JOIN works ON works.id = recordings.work WHERE works.composer IS ?1 AND (persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9")
.unwrap() .unwrap()
.query_map([&person.id, &search], Person::from_row) .query_map([&composer.id, &search], Person::from_row)
.unwrap() .unwrap()
.collect::<rusqlite::Result<Vec<Person>>>() .collect::<rusqlite::Result<Vec<Person>>>()
.unwrap(); .unwrap();
@ -106,7 +117,7 @@ impl MusicusLibrary {
.con() .con()
.prepare("SELECT DISTINCT ensembles.id, ensembles.name FROM ensembles INNER JOIN performances ON performances.ensemble = ensembles.id INNER JOIN recordings ON recordings.id = performances.recording INNER JOIN works ON works.id = recordings.work WHERE works.composer IS ?1 AND ensembles.name LIKE ?2 LIMIT 9") .prepare("SELECT DISTINCT ensembles.id, ensembles.name FROM ensembles INNER JOIN performances ON performances.ensemble = ensembles.id INNER JOIN recordings ON recordings.id = performances.recording INNER JOIN works ON works.id = recordings.work WHERE works.composer IS ?1 AND ensembles.name LIKE ?2 LIMIT 9")
.unwrap() .unwrap()
.query_map([&person.id, &search], Ensemble::from_row) .query_map([&composer.id, &search], Ensemble::from_row)
.unwrap() .unwrap()
.collect::<rusqlite::Result<Vec<Ensemble>>>() .collect::<rusqlite::Result<Vec<Ensemble>>>()
.unwrap(); .unwrap();
@ -115,34 +126,26 @@ impl MusicusLibrary {
.con() .con()
.prepare("SELECT DISTINCT works.id, works.title, persons.id, persons.first_name, persons.last_name FROM works INNER JOIN persons ON works.composer = persons.id WHERE works.composer = ?1 AND title LIKE ?2 LIMIT 9") .prepare("SELECT DISTINCT works.id, works.title, persons.id, persons.first_name, persons.last_name FROM works INNER JOIN persons ON works.composer = persons.id WHERE works.composer = ?1 AND title LIKE ?2 LIMIT 9")
.unwrap() .unwrap()
.query_map([&person.id, &search], Work::from_row) .query_map([&composer.id, &search], Work::from_row)
.unwrap() .unwrap()
.collect::<rusqlite::Result<Vec<Work>>>() .collect::<rusqlite::Result<Vec<Work>>>()
.unwrap(); .unwrap();
let recordings = self
.con()
.prepare("SELECT DISTINCT recordings.id, works.id, works.title, persons.id, persons.first_name, persons.last_name FROM recordings INNER JOIN works ON recordings.work = works.id INNER JOIN persons ON works.composer = persons.id INNER JOIN performances ON recordings.id = performances.recording WHERE performances.person IS ?1 AND (works.title LIKE ?2 OR persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9")
.unwrap()
.query_map([&person.id, &search], Recording::from_row)
.unwrap()
.collect::<rusqlite::Result<Vec<Recording>>>()
.unwrap();
LibraryResults { LibraryResults {
persons, performers,
ensembles, ensembles,
works, works,
recordings, ..Default::default()
} }
} }
LibraryQuery { LibraryQuery {
person: None, composer: None,
performer: None,
ensemble: Some(ensemble), ensemble: Some(ensemble),
work: None, work: None,
.. ..
} => { } => {
let persons = self.con() let composers = self.con()
.prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN works ON works.composer = persons.id INNER JOIN recordings ON recordings.work = works.id INNER JOIN performances ON performances.recording = recordings.id WHERE performances.ensemble IS ?1 AND (persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9") .prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN works ON works.composer = persons.id INNER JOIN recordings ON recordings.work = works.id INNER JOIN performances ON performances.recording = recordings.id WHERE performances.ensemble IS ?1 AND (persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9")
.unwrap() .unwrap()
.query_map([&ensemble.id, &search], Person::from_row) .query_map([&ensemble.id, &search], Person::from_row)
@ -160,22 +163,71 @@ impl MusicusLibrary {
.unwrap(); .unwrap();
LibraryResults { LibraryResults {
persons, composers,
recordings, recordings,
..Default::default() ..Default::default()
} }
}, },
LibraryQuery { LibraryQuery {
person: Some(person), composer: None,
performer: Some(performer),
work: None,
..
} => {
let composers = self.con()
.prepare("SELECT DISTINCT persons.id, persons.first_name, persons.last_name FROM persons INNER JOIN works ON works.composer = persons.id INNER JOIN recordings ON recordings.work = works.id INNER JOIN performances ON performances.recording = recordings.id WHERE performances.person IS ?1 AND (persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9")
.unwrap()
.query_map([&performer.id, &search], Person::from_row)
.unwrap()
.collect::<rusqlite::Result<Vec<Person>>>()
.unwrap();
let recordings = self
.con()
.prepare("SELECT DISTINCT recordings.id, works.id, works.title, persons.id, persons.first_name, persons.last_name FROM recordings INNER JOIN works ON recordings.work = works.id INNER JOIN persons ON works.composer = persons.id INNER JOIN performances ON recordings.id = performances.recording WHERE performances.person IS ?1 AND (works.title LIKE ?2 OR persons.first_name LIKE ?2 OR persons.last_name LIKE ?2) LIMIT 9")
.unwrap()
.query_map([&performer.id, &search], Recording::from_row)
.unwrap()
.collect::<rusqlite::Result<Vec<Recording>>>()
.unwrap();
LibraryResults {
composers,
recordings,
..Default::default()
}
},
LibraryQuery {
composer: Some(composer),
ensemble: Some(ensemble), ensemble: Some(ensemble),
work: None, work: None,
.. ..
} => { } => {
let recordings = self let recordings = self
.con() .con()
.prepare("SELECT DISTINCT recordings.id, works.id, works.title, persons.id, persons.first_name, persons.last_name FROM recordings INNER JOIN works ON recordings.work = works.id INNER JOIN persons ON works.composer = persons.id INNER JOIN performances ON recordings.id = performances.recording WHERE works.composer IS ?1 AND performances.ensemble IS ?2 AND (works.title LIKE ?3 OR persons.first_name LIKE ?3 OR persons.last_name LIKE ?3) LIMIT 9") .prepare("SELECT DISTINCT recordings.id, works.id, works.title, persons.id, persons.first_name, persons.last_name FROM recordings INNER JOIN works ON recordings.work = works.id INNER JOIN persons ON works.composer = persons.id INNER JOIN performances ON recordings.id = performances.recording WHERE works.composer IS ?1 AND performances.ensemble IS ?2 AND works.title LIKE ?3 LIMIT 9")
.unwrap() .unwrap()
.query_map([&person.id, &ensemble.id, &search], Recording::from_row) .query_map([&composer.id, &ensemble.id, &search], Recording::from_row)
.unwrap()
.collect::<rusqlite::Result<Vec<Recording>>>()
.unwrap();
LibraryResults {
recordings,
..Default::default()
}
},
LibraryQuery {
composer: Some(composer),
performer: Some(performer),
work: None,
..
} => {
let recordings = self
.con()
.prepare("SELECT DISTINCT recordings.id, works.id, works.title, persons.id, persons.first_name, persons.last_name FROM recordings INNER JOIN works ON recordings.work = works.id INNER JOIN persons ON works.composer = persons.id INNER JOIN performances ON recordings.id = performances.recording WHERE works.composer IS ?1 AND performances.person IS ?2 AND works.title LIKE ?3 LIMIT 9")
.unwrap()
.query_map([&composer.id, &performer.id, &search], Recording::from_row)
.unwrap() .unwrap()
.collect::<rusqlite::Result<Vec<Recording>>>() .collect::<rusqlite::Result<Vec<Recording>>>()
.unwrap(); .unwrap();
@ -213,7 +265,8 @@ impl MusicusLibrary {
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct LibraryQuery { pub struct LibraryQuery {
pub person: Option<Person>, pub composer: Option<Person>,
pub performer: Option<Person>,
pub ensemble: Option<Ensemble>, pub ensemble: Option<Ensemble>,
pub work: Option<Work>, pub work: Option<Work>,
pub search: String, pub search: String,
@ -221,7 +274,8 @@ pub struct LibraryQuery {
#[derive(Default, Debug)] #[derive(Default, Debug)]
pub struct LibraryResults { pub struct LibraryResults {
pub persons: Vec<Person>, pub composers: Vec<Person>,
pub performers: Vec<Person>,
pub ensembles: Vec<Ensemble>, pub ensembles: Vec<Ensemble>,
pub works: Vec<Work>, pub works: Vec<Work>,
pub recordings: Vec<Recording>, pub recordings: Vec<Recording>,
@ -229,7 +283,8 @@ pub struct LibraryResults {
impl LibraryResults { impl LibraryResults {
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
self.persons.is_empty() self.composers.is_empty()
&& self.performers.is_empty()
&& self.ensembles.is_empty() && self.ensembles.is_empty()
&& self.works.is_empty() && self.works.is_empty()
&& self.recordings.is_empty() && self.recordings.is_empty()

View file

@ -160,7 +160,8 @@ impl MusicusSearchEntry {
for tag in &*self.imp().tags.borrow() { for tag in &*self.imp().tags.borrow() {
match tag.tag().clone() { match tag.tag().clone() {
Tag::Person(person) => query.person = Some(person), Tag::Composer(person) => query.composer = Some(person),
Tag::Performer(person) => query.performer = Some(person),
Tag::Ensemble(ensemble) => query.ensemble = Some(ensemble), Tag::Ensemble(ensemble) => query.ensemble = Some(ensemble),
Tag::Work(work) => query.work = Some(work), Tag::Work(work) => query.work = Some(work),
} }

View file

@ -54,7 +54,8 @@ impl MusicusSearchTag {
let obj: MusicusSearchTag = glib::Object::new(); let obj: MusicusSearchTag = glib::Object::new();
obj.imp().label.set_label(&match &tag { obj.imp().label.set_label(&match &tag {
Tag::Person(person) => person.name_fl(), Tag::Composer(person) => person.name_fl(),
Tag::Performer(person) => person.name_fl(),
Tag::Ensemble(ensemble) => ensemble.name.clone(), Tag::Ensemble(ensemble) => ensemble.name.clone(),
Tag::Work(work) => work.title.clone(), Tag::Work(work) => work.title.clone(),
}); });
@ -76,7 +77,8 @@ impl MusicusSearchTag {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum Tag { pub enum Tag {
Person(Person), Composer(Person),
Performer(Person),
Ensemble(Ensemble), Ensemble(Ensemble),
Work(Work), Work(Work),
} }