mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
New search page
This commit is contained in:
parent
a6e0935df8
commit
cff489f43e
12 changed files with 830 additions and 793 deletions
132
src/album_page.rs
Normal file
132
src/album_page.rs
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
use std::cell::OnceCell;
|
||||
|
||||
use adw::subclass::prelude::*;
|
||||
use gtk::{
|
||||
glib::{self, Properties},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
db::models::*, editor::album::AlbumEditor, library::Library, player::Player,
|
||||
playlist_item::PlaylistItem, recording_tile::RecordingTile,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||
#[properties(wrapper_type = super::AlbumPage)]
|
||||
#[template(file = "data/ui/album_page.blp")]
|
||||
pub struct AlbumPage {
|
||||
#[property(get, construct_only)]
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub library: OnceCell<Library>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub player: OnceCell<Player>,
|
||||
|
||||
pub album: OnceCell<Album>,
|
||||
|
||||
#[template_child]
|
||||
pub title_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub subtitle_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub recordings_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AlbumPage {
|
||||
const NAME: &'static str = "MusicusAlbumPage";
|
||||
type Type = super::AlbumPage;
|
||||
type ParentType = adw::NavigationPage;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for AlbumPage {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for AlbumPage {}
|
||||
impl NavigationPageImpl for AlbumPage {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct AlbumPage(ObjectSubclass<imp::AlbumPage>)
|
||||
@extends gtk::Widget, adw::NavigationPage;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl AlbumPage {
|
||||
pub fn new(
|
||||
navigation: &adw::NavigationView,
|
||||
library: &Library,
|
||||
player: &Player,
|
||||
album: Album,
|
||||
) -> Self {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.property("player", player)
|
||||
.build();
|
||||
|
||||
obj.imp().title_label.set_label(&album.to_string());
|
||||
obj.imp().subtitle_label.set_label(&album.performers_string());
|
||||
|
||||
for recording in &album.recordings {
|
||||
obj.imp()
|
||||
.recordings_flow_box
|
||||
.append(&RecordingTile::new(navigation, library, recording));
|
||||
}
|
||||
|
||||
obj.imp().album.set(album).unwrap();
|
||||
|
||||
obj
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn edit_button_clicked(&self) {
|
||||
self.navigation().push(&AlbumEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(&self.imp().album.get().unwrap().clone()),
|
||||
));
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn play_button_clicked(&self) {
|
||||
let playlist = self
|
||||
.imp()
|
||||
.album
|
||||
.get()
|
||||
.unwrap()
|
||||
.recordings
|
||||
.iter()
|
||||
.map(|r| self.player().recording_to_playlist(r))
|
||||
.flatten()
|
||||
.collect::<Vec<PlaylistItem>>();
|
||||
|
||||
self.player().append_and_play(playlist);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn recording_selected(&self, tile: >k::FlowBoxChild) {
|
||||
let playlist = self
|
||||
.player()
|
||||
.recording_to_playlist(tile.downcast_ref::<RecordingTile>().unwrap().recording());
|
||||
self.player().append_and_play(playlist);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
//! This module contains higher-level models combining information from
|
||||
//! multiple database tables.
|
||||
|
||||
use std::fmt::Display;
|
||||
use std::{collections::HashSet, fmt::Display};
|
||||
|
||||
use anyhow::Result;
|
||||
use diesel::prelude::*;
|
||||
|
|
@ -392,6 +392,27 @@ impl Album {
|
|||
recordings,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn performers_string(&self) -> String {
|
||||
let mut performers = HashSet::new();
|
||||
let mut ensembles = HashSet::new();
|
||||
|
||||
for recording in &self.recordings {
|
||||
for performer in &recording.persons {
|
||||
performers.insert(performer.to_string());
|
||||
}
|
||||
|
||||
for ensemble in &recording.ensembles {
|
||||
ensembles.insert(ensemble.to_string());
|
||||
}
|
||||
}
|
||||
|
||||
performers
|
||||
.into_iter()
|
||||
.chain(ensembles)
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Album {}
|
||||
|
|
|
|||
389
src/home_page.rs
389
src/home_page.rs
|
|
@ -1,389 +0,0 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::subclass::{navigation_page::NavigationPageImpl, prelude::*};
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, Properties},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
album_tile::AlbumTile,
|
||||
config,
|
||||
db::models::*,
|
||||
editor::{
|
||||
ensemble::EnsembleEditor, instrument::InstrumentEditor, person::PersonEditor,
|
||||
work::WorkEditor,
|
||||
},
|
||||
library::{Library, LibraryQuery},
|
||||
player::Player,
|
||||
program::Program,
|
||||
program_tile::ProgramTile,
|
||||
recording_tile::RecordingTile,
|
||||
search_entry::SearchEntry,
|
||||
search_tag::Tag,
|
||||
tag_tile::TagTile,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||
#[properties(wrapper_type = super::HomePage)]
|
||||
#[template(file = "data/ui/home_page.blp")]
|
||||
pub struct HomePage {
|
||||
#[property(get, construct_only)]
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub library: OnceCell<Library>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub player: OnceCell<Player>,
|
||||
|
||||
pub programs: RefCell<Vec<Program>>,
|
||||
pub composers: RefCell<Vec<Person>>,
|
||||
pub performers: RefCell<Vec<Person>>,
|
||||
pub ensembles: RefCell<Vec<Ensemble>>,
|
||||
pub instruments: RefCell<Vec<Instrument>>,
|
||||
pub works: RefCell<Vec<Work>>,
|
||||
pub recordings: RefCell<Vec<Recording>>,
|
||||
pub albums: RefCell<Vec<Album>>,
|
||||
|
||||
#[template_child]
|
||||
pub search_entry: TemplateChild<SearchEntry>,
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub header_box: TemplateChild<gtk::Box>,
|
||||
#[template_child]
|
||||
pub title_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub subtitle_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub programs_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub composers_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub performers_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub ensembles_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub instruments_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub works_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[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>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for HomePage {
|
||||
const NAME: &'static str = "MusicusHomePage";
|
||||
type Type = super::HomePage;
|
||||
type ParentType = adw::NavigationPage;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for HomePage {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
self.search_entry.set_key_capture_widget(&*self.obj());
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
self.search_entry.connect_query_changed(move |entry| {
|
||||
obj.query(&entry.query());
|
||||
});
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
self.library.get().unwrap().connect_changed(move |_| {
|
||||
obj.imp().search_entry.reset();
|
||||
});
|
||||
|
||||
self.player
|
||||
.get()
|
||||
.unwrap()
|
||||
.bind_property("active", &self.play_button.get(), "visible")
|
||||
.invert_boolean()
|
||||
.sync_create()
|
||||
.build();
|
||||
|
||||
let settings = gio::Settings::new(&config::APP_ID);
|
||||
|
||||
let programs = vec![
|
||||
Program::deserialize(&settings.string("program1")).unwrap(),
|
||||
Program::deserialize(&settings.string("program2")).unwrap(),
|
||||
Program::deserialize(&settings.string("program3")).unwrap(),
|
||||
];
|
||||
|
||||
for program in &programs {
|
||||
self.programs_flow_box
|
||||
.append(&ProgramTile::new(program.to_owned()));
|
||||
}
|
||||
|
||||
self.programs.replace(programs);
|
||||
|
||||
self.obj().query(&LibraryQuery::default());
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for HomePage {}
|
||||
impl NavigationPageImpl for HomePage {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct HomePage(ObjectSubclass<imp::HomePage>)
|
||||
@extends gtk::Widget, adw::NavigationPage;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl HomePage {
|
||||
pub fn new(navigation: &adw::NavigationView, library: &Library, player: &Player) -> Self {
|
||||
glib::Object::builder()
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.property("player", player)
|
||||
.build()
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn back_button_clicked(&self) {
|
||||
self.imp().search_entry.reset();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn edit_button_clicked(&self) {
|
||||
if let Some(tag) = self.imp().search_entry.tags().first() {
|
||||
match tag {
|
||||
Tag::Composer(person) | Tag::Performer(person) => {
|
||||
self.navigation().push(&PersonEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(person),
|
||||
));
|
||||
}
|
||||
Tag::Ensemble(ensemble) => {
|
||||
self.navigation().push(&EnsembleEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(ensemble),
|
||||
));
|
||||
}
|
||||
Tag::Instrument(instrument) => self.navigation().push(&InstrumentEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(instrument),
|
||||
)),
|
||||
Tag::Work(work) => self.navigation().push(&WorkEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(work),
|
||||
false,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn play(&self) {
|
||||
let program = Program::from_query(self.imp().search_entry.query());
|
||||
self.player().set_program(program);
|
||||
|
||||
self.player().play();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn select(&self, search_entry: &SearchEntry) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.programs_flow_box.is_visible() {
|
||||
if let Some(program) = imp.programs.borrow().first().cloned() {
|
||||
self.player().set_program(program);
|
||||
}
|
||||
} else {
|
||||
let (composer, performer, ensemble, instrument, work, recording, album) = {
|
||||
(
|
||||
imp.composers.borrow().first().cloned(),
|
||||
imp.performers.borrow().first().cloned(),
|
||||
imp.ensembles.borrow().first().cloned(),
|
||||
imp.instruments.borrow().first().cloned(),
|
||||
imp.works.borrow().first().cloned(),
|
||||
imp.recordings.borrow().first().cloned(),
|
||||
imp.albums.borrow().first().cloned(),
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(person) = composer {
|
||||
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 {
|
||||
search_entry.add_tag(Tag::Ensemble(ensemble));
|
||||
} else if let Some(instrument) = instrument {
|
||||
search_entry.add_tag(Tag::Instrument(instrument));
|
||||
} else if let Some(work) = work {
|
||||
search_entry.add_tag(Tag::Work(work));
|
||||
} else if let Some(recording) = recording {
|
||||
self.player().play_recording(&recording);
|
||||
} else if let Some(album) = album {
|
||||
self.show_album(&album);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn program_selected(&self, tile: >k::FlowBoxChild, _: >k::FlowBox) {
|
||||
self.player()
|
||||
.set_program(tile.downcast_ref::<ProgramTile>().unwrap().program());
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn tile_selected(&self, tile: >k::FlowBoxChild, _: >k::FlowBox) {
|
||||
self.imp()
|
||||
.search_entry
|
||||
.add_tag(tile.downcast_ref::<TagTile>().unwrap().tag().clone())
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn recording_selected(&self, tile: >k::FlowBoxChild, _: >k::FlowBox) {
|
||||
self.player()
|
||||
.play_recording(tile.downcast_ref::<RecordingTile>().unwrap().recording());
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn album_selected(&self, tile: >k::FlowBoxChild, _: >k::FlowBox) {
|
||||
self.show_album(tile.downcast_ref::<AlbumTile>().unwrap().album());
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
for flowbox in [
|
||||
&imp.composers_flow_box,
|
||||
&imp.performers_flow_box,
|
||||
&imp.ensembles_flow_box,
|
||||
&imp.instruments_flow_box,
|
||||
&imp.works_flow_box,
|
||||
&imp.recordings_flow_box,
|
||||
&imp.albums_flow_box,
|
||||
] {
|
||||
while let Some(widget) = flowbox.first_child() {
|
||||
flowbox.remove(&widget);
|
||||
}
|
||||
}
|
||||
|
||||
imp.programs_flow_box.set_visible(query.is_empty());
|
||||
|
||||
if let Some(tag) = imp.search_entry.tags().first() {
|
||||
match tag {
|
||||
Tag::Composer(person) | Tag::Performer(person) => {
|
||||
imp.title_label.set_text(&person.name.get());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
}
|
||||
Tag::Ensemble(ensemble) => {
|
||||
imp.title_label.set_text(&ensemble.name.get());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
}
|
||||
Tag::Instrument(instrument) => {
|
||||
imp.title_label.set_text(&instrument.name.get());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
}
|
||||
Tag::Work(work) => {
|
||||
imp.title_label.set_text(&work.name.get());
|
||||
if let Some(composers) = work.composers_string() {
|
||||
imp.subtitle_label.set_text(&composers);
|
||||
imp.subtitle_label.set_visible(true);
|
||||
} else {
|
||||
imp.subtitle_label.set_visible(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
imp.header_box.set_visible(true);
|
||||
} else {
|
||||
imp.header_box.set_visible(false);
|
||||
}
|
||||
|
||||
if results.is_empty() {
|
||||
imp.stack.set_visible_child_name("empty");
|
||||
} else {
|
||||
imp.stack.set_visible_child_name("results");
|
||||
|
||||
imp.composers_flow_box
|
||||
.set_visible(!results.composers.is_empty());
|
||||
imp.performers_flow_box
|
||||
.set_visible(!results.performers.is_empty());
|
||||
imp.ensembles_flow_box
|
||||
.set_visible(!results.ensembles.is_empty());
|
||||
imp.instruments_flow_box
|
||||
.set_visible(!results.instruments.is_empty());
|
||||
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
|
||||
.append(&TagTile::new(Tag::Composer(composer.clone())));
|
||||
}
|
||||
|
||||
for performer in &results.performers {
|
||||
imp.performers_flow_box
|
||||
.append(&TagTile::new(Tag::Performer(performer.clone())));
|
||||
}
|
||||
|
||||
for ensemble in &results.ensembles {
|
||||
imp.ensembles_flow_box
|
||||
.append(&TagTile::new(Tag::Ensemble(ensemble.clone())));
|
||||
}
|
||||
|
||||
for instrument in &results.instruments {
|
||||
imp.instruments_flow_box
|
||||
.append(&TagTile::new(Tag::Instrument(instrument.clone())));
|
||||
}
|
||||
|
||||
for work in &results.works {
|
||||
imp.works_flow_box
|
||||
.append(&TagTile::new(Tag::Work(work.clone())));
|
||||
}
|
||||
|
||||
for recording in &results.recordings {
|
||||
imp.recordings_flow_box.append(&RecordingTile::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
recording,
|
||||
));
|
||||
}
|
||||
|
||||
for album in &results.albums {
|
||||
imp.albums_flow_box.append(&AlbumTile::new(album));
|
||||
}
|
||||
|
||||
imp.composers.replace(results.composers);
|
||||
imp.performers.replace(results.performers);
|
||||
imp.ensembles.replace(results.ensembles);
|
||||
imp.instruments.replace(results.instruments);
|
||||
imp.works.replace(results.works);
|
||||
imp.recordings.replace(results.recordings);
|
||||
imp.albums.replace(results.albums);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -72,8 +72,8 @@ impl Library {
|
|||
.build()
|
||||
}
|
||||
|
||||
pub fn query(&self, query: &LibraryQuery) -> Result<LibraryResults> {
|
||||
let search = format!("%{}%", query.search);
|
||||
pub fn search(&self, query: &LibraryQuery, search: &str) -> Result<LibraryResults> {
|
||||
let search = format!("%{}%", search);
|
||||
let mut binding = self.imp().connection.borrow_mut();
|
||||
let connection = &mut *binding.as_mut().unwrap();
|
||||
|
||||
|
|
@ -1541,14 +1541,13 @@ impl Library {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Clone, Default, Debug)]
|
||||
pub struct LibraryQuery {
|
||||
pub composer: Option<Person>,
|
||||
pub performer: Option<Person>,
|
||||
pub ensemble: Option<Ensemble>,
|
||||
pub instrument: Option<Instrument>,
|
||||
pub work: Option<Work>,
|
||||
pub search: String,
|
||||
}
|
||||
|
||||
impl LibraryQuery {
|
||||
|
|
@ -1558,7 +1557,6 @@ impl LibraryQuery {
|
|||
&& self.ensemble.is_none()
|
||||
&& self.instrument.is_none()
|
||||
&& self.work.is_none()
|
||||
&& self.search.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
mod album_page;
|
||||
mod album_tile;
|
||||
mod application;
|
||||
mod config;
|
||||
mod db;
|
||||
mod editor;
|
||||
mod home_page;
|
||||
mod search_page;
|
||||
mod library;
|
||||
mod library_manager;
|
||||
mod player;
|
||||
|
|
@ -14,7 +15,6 @@ mod playlist_tile;
|
|||
mod program;
|
||||
mod program_tile;
|
||||
mod recording_tile;
|
||||
mod search_entry;
|
||||
mod search_tag;
|
||||
mod selector;
|
||||
mod tag_tile;
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ impl Player {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn play_recording(&self, recording: &Recording) {
|
||||
pub fn recording_to_playlist(&self, recording: &Recording) -> Vec<PlaylistItem> {
|
||||
let tracks = &self
|
||||
.library()
|
||||
.unwrap()
|
||||
|
|
@ -211,8 +211,8 @@ impl Player {
|
|||
.unwrap();
|
||||
|
||||
if tracks.is_empty() {
|
||||
log::warn!("Ignoring recording without tracks being added to the playlist.");
|
||||
return;
|
||||
log::warn!("Recording without tracks: {}.", &recording.recording_id);
|
||||
return Vec::new();
|
||||
}
|
||||
|
||||
let performances = recording.performers_string();
|
||||
|
|
@ -272,14 +272,14 @@ impl Player {
|
|||
}
|
||||
}
|
||||
|
||||
self.append(items);
|
||||
items
|
||||
}
|
||||
|
||||
pub fn append(&self, tracks: Vec<PlaylistItem>) {
|
||||
pub fn append(&self, items: Vec<PlaylistItem>) {
|
||||
let playlist = self.playlist();
|
||||
|
||||
for track in tracks {
|
||||
playlist.append(&track);
|
||||
for item in items {
|
||||
playlist.append(&item);
|
||||
}
|
||||
|
||||
if !self.active() && playlist.n_items() > 0 {
|
||||
|
|
@ -289,6 +289,21 @@ impl Player {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn append_and_play(&self, items: Vec<PlaylistItem>) {
|
||||
let playlist = self.playlist();
|
||||
let first_index = playlist.n_items();
|
||||
|
||||
for item in items {
|
||||
playlist.append(&item);
|
||||
}
|
||||
|
||||
if playlist.n_items() > first_index {
|
||||
self.set_active(true);
|
||||
self.set_current_index(first_index);
|
||||
self.play();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn play_pause(&self) {
|
||||
if self.playing() {
|
||||
self.pause();
|
||||
|
|
@ -423,7 +438,8 @@ impl Player {
|
|||
if let Some(library) = self.library() {
|
||||
// TODO: if program.play_full_recordings() {
|
||||
let recording = library.generate_recording(program).unwrap();
|
||||
self.play_recording(&recording);
|
||||
let playlist = self.recording_to_playlist(&recording);
|
||||
self.append(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,267 +0,0 @@
|
|||
use std::{cell::RefCell, time::Duration};
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::{
|
||||
gdk, gio,
|
||||
glib::{self, clone, subclass::Signal, Propagation},
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::{
|
||||
library::LibraryQuery,
|
||||
search_tag::{SearchTag, Tag},
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/search_entry.blp")]
|
||||
pub struct SearchEntry {
|
||||
#[template_child]
|
||||
pub tags_box: TemplateChild<gtk::Box>,
|
||||
#[template_child]
|
||||
pub text: TemplateChild<gtk::Text>,
|
||||
#[template_child]
|
||||
pub clear_icon: TemplateChild<gtk::Image>,
|
||||
|
||||
pub tags: RefCell<Vec<SearchTag>>,
|
||||
pub query_changed: RefCell<Option<gio::Cancellable>>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SearchEntry {
|
||||
const NAME: &'static str = "MusicusSearchEntry";
|
||||
type Type = super::SearchEntry;
|
||||
type ParentType = gtk::Box;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
klass.set_css_name("entry");
|
||||
|
||||
klass.add_shortcut(
|
||||
>k::Shortcut::builder()
|
||||
.trigger(>k::KeyvalTrigger::new(
|
||||
gdk::Key::Escape,
|
||||
gdk::ModifierType::empty(),
|
||||
))
|
||||
.action(>k::CallbackAction::new(|widget, _| match widget
|
||||
.downcast_ref::<super::SearchEntry>()
|
||||
{
|
||||
Some(obj) => {
|
||||
obj.reset();
|
||||
Propagation::Stop
|
||||
}
|
||||
None => Propagation::Proceed,
|
||||
}))
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for SearchEntry {
|
||||
fn constructed(&self) {
|
||||
let controller = gtk::GestureClick::new();
|
||||
|
||||
controller.connect_pressed(|gesture, _, _, _| {
|
||||
gesture.set_state(gtk::EventSequenceState::Claimed);
|
||||
});
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
controller.connect_released(move |_, _, _, _| {
|
||||
obj.reset();
|
||||
});
|
||||
|
||||
self.clear_icon.add_controller(controller);
|
||||
}
|
||||
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: Lazy<Vec<Signal>> = Lazy::new(|| {
|
||||
vec![
|
||||
Signal::builder("activate").build(),
|
||||
Signal::builder("query-changed").build(),
|
||||
]
|
||||
});
|
||||
|
||||
SIGNALS.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for SearchEntry {
|
||||
fn grab_focus(&self) -> bool {
|
||||
self.text.grab_focus_without_selecting()
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxImpl for SearchEntry {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SearchEntry(ObjectSubclass<imp::SearchEntry>)
|
||||
@extends gtk::Widget;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl SearchEntry {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::new()
|
||||
}
|
||||
|
||||
pub fn connect_query_changed<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
|
||||
self.connect_local("query-changed", true, move |values| {
|
||||
let obj = values[0].get::<Self>().unwrap();
|
||||
f(&obj);
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub fn set_key_capture_widget(&self, widget: &impl IsA<gtk::Widget>) {
|
||||
let controller = gtk::EventControllerKey::new();
|
||||
|
||||
controller.connect_key_pressed(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
#[upgrade_or]
|
||||
glib::Propagation::Proceed,
|
||||
move |controller, _, _, _| {
|
||||
match controller.forward(&this.imp().text.get()) {
|
||||
true => {
|
||||
this.grab_focus();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
false => glib::Propagation::Proceed,
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
controller.connect_key_released(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |controller, _, _, _| {
|
||||
controller.forward(&this.imp().text.get());
|
||||
}
|
||||
));
|
||||
|
||||
widget.add_controller(controller);
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
{
|
||||
let mut tags = self.imp().tags.borrow_mut();
|
||||
while let Some(tag) = tags.pop() {
|
||||
self.imp().tags_box.remove(&tag);
|
||||
}
|
||||
}
|
||||
|
||||
self.imp().text.set_text("");
|
||||
self.emit_by_name::<()>("query-changed", &[]);
|
||||
}
|
||||
|
||||
pub fn add_tag(&self, tag: Tag) {
|
||||
let imp = self.imp();
|
||||
|
||||
imp.clear_icon.set_visible(true);
|
||||
imp.text.set_text("");
|
||||
|
||||
let tag = SearchTag::new(tag);
|
||||
|
||||
tag.connect_remove(clone!(
|
||||
#[weak(rename_to = this)]
|
||||
self,
|
||||
move |tag| {
|
||||
let imp = this.imp();
|
||||
|
||||
imp.tags_box.remove(tag);
|
||||
|
||||
{
|
||||
imp.tags.borrow_mut().retain(|t| t.tag() != tag.tag());
|
||||
}
|
||||
|
||||
this.emit_by_name::<()>("query-changed", &[]);
|
||||
}
|
||||
));
|
||||
|
||||
imp.tags_box.append(&tag);
|
||||
imp.tags.borrow_mut().push(tag);
|
||||
self.emit_by_name::<()>("query-changed", &[]);
|
||||
}
|
||||
|
||||
pub fn tags(&self) -> Vec<Tag> {
|
||||
self.imp()
|
||||
.tags
|
||||
.borrow()
|
||||
.iter()
|
||||
.map(|t| t.tag().to_owned())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn query(&self) -> LibraryQuery {
|
||||
let mut query = LibraryQuery {
|
||||
search: self.imp().text.text().to_string(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
for tag in &*self.imp().tags.borrow() {
|
||||
match tag.tag().clone() {
|
||||
Tag::Composer(person) => query.composer = Some(person),
|
||||
Tag::Performer(person) => query.performer = Some(person),
|
||||
Tag::Ensemble(ensemble) => query.ensemble = Some(ensemble),
|
||||
Tag::Instrument(instrument) => query.instrument = Some(instrument),
|
||||
Tag::Work(work) => query.work = Some(work),
|
||||
}
|
||||
}
|
||||
|
||||
query
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn activate(&self, _: >k::Text) {
|
||||
self.emit_by_name::<()>("activate", &[]);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn backspace(&self, text: >k::Text) {
|
||||
if text.position() == 0 {
|
||||
let changed = if let Some(tag) = self.imp().tags.borrow_mut().pop() {
|
||||
self.imp().tags_box.remove(&tag);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if changed {
|
||||
self.emit_by_name::<()>("query-changed", &[]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
async fn text_changed(&self, text: >k::Text) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.tags.borrow().is_empty() {
|
||||
imp.clear_icon.set_visible(!text.text().is_empty());
|
||||
}
|
||||
|
||||
if let Some(cancellable) = imp.query_changed.borrow_mut().take() {
|
||||
cancellable.cancel();
|
||||
}
|
||||
|
||||
let cancellable = gio::Cancellable::new();
|
||||
imp.query_changed.replace(Some(cancellable.clone()));
|
||||
|
||||
let _ = gio::CancellableFuture::new(
|
||||
async {
|
||||
glib::timeout_future(Duration::from_millis(150)).await;
|
||||
self.emit_by_name::<()>("query-changed", &[]);
|
||||
},
|
||||
cancellable,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
478
src/search_page.rs
Normal file
478
src/search_page.rs
Normal file
|
|
@ -0,0 +1,478 @@
|
|||
use std::cell::{OnceCell, RefCell};
|
||||
|
||||
use adw::subclass::{navigation_page::NavigationPageImpl, prelude::*};
|
||||
use formatx::formatx;
|
||||
use gettextrs::gettext;
|
||||
use gtk::{
|
||||
gio,
|
||||
glib::{self, Properties},
|
||||
prelude::*,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
album_page::AlbumPage,
|
||||
album_tile::AlbumTile,
|
||||
config,
|
||||
db::models::*,
|
||||
editor::{
|
||||
ensemble::EnsembleEditor, instrument::InstrumentEditor, person::PersonEditor,
|
||||
work::WorkEditor,
|
||||
},
|
||||
library::{Library, LibraryQuery},
|
||||
player::Player,
|
||||
program::Program,
|
||||
program_tile::ProgramTile,
|
||||
recording_tile::RecordingTile,
|
||||
search_tag::Tag,
|
||||
tag_tile::TagTile,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Properties, Debug, Default, gtk::CompositeTemplate)]
|
||||
#[properties(wrapper_type = super::SearchPage)]
|
||||
#[template(file = "data/ui/search_page.blp")]
|
||||
pub struct SearchPage {
|
||||
#[property(get, construct_only)]
|
||||
pub navigation: OnceCell<adw::NavigationView>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub library: OnceCell<Library>,
|
||||
|
||||
#[property(get, construct_only)]
|
||||
pub player: OnceCell<Player>,
|
||||
|
||||
pub query: OnceCell<LibraryQuery>,
|
||||
pub highlight: RefCell<Option<Tag>>,
|
||||
|
||||
pub programs: RefCell<Vec<Program>>,
|
||||
pub composers: RefCell<Vec<Person>>,
|
||||
pub performers: RefCell<Vec<Person>>,
|
||||
pub ensembles: RefCell<Vec<Ensemble>>,
|
||||
pub instruments: RefCell<Vec<Instrument>>,
|
||||
pub works: RefCell<Vec<Work>>,
|
||||
pub recordings: RefCell<Vec<Recording>>,
|
||||
pub albums: RefCell<Vec<Album>>,
|
||||
|
||||
#[template_child]
|
||||
pub scrolled_window: TemplateChild<gtk::ScrolledWindow>,
|
||||
#[template_child]
|
||||
pub header_bar: TemplateChild<adw::HeaderBar>,
|
||||
#[template_child]
|
||||
pub search_entry: TemplateChild<gtk::SearchEntry>,
|
||||
#[template_child]
|
||||
pub stack: TemplateChild<gtk::Stack>,
|
||||
#[template_child]
|
||||
pub header_box: TemplateChild<gtk::Box>,
|
||||
#[template_child]
|
||||
pub title_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub subtitle_label: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub programs_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub composers_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub performers_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub ensembles_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub instruments_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub works_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub recordings_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
#[template_child]
|
||||
pub albums_flow_box: TemplateChild<gtk::FlowBox>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SearchPage {
|
||||
const NAME: &'static str = "MusicusSearchPage";
|
||||
type Type = super::SearchPage;
|
||||
type ParentType = adw::NavigationPage;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for SearchPage {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
self.search_entry.set_key_capture_widget(Some(&*self.obj()));
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
self.search_entry.connect_search_changed(move |entry| {
|
||||
obj.imp().scrolled_window.vadjustment().set_value(0.0);
|
||||
obj.search(&entry.text());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for SearchPage {
|
||||
fn map(&self) {
|
||||
self.parent_map();
|
||||
self.search_entry.grab_focus();
|
||||
}
|
||||
}
|
||||
|
||||
impl NavigationPageImpl for SearchPage {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SearchPage(ObjectSubclass<imp::SearchPage>)
|
||||
@extends gtk::Widget, adw::NavigationPage;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl SearchPage {
|
||||
pub fn new(
|
||||
navigation: &adw::NavigationView,
|
||||
library: &Library,
|
||||
player: &Player,
|
||||
query: LibraryQuery,
|
||||
) -> Self {
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("navigation", navigation)
|
||||
.property("library", library)
|
||||
.property("player", player)
|
||||
.build();
|
||||
|
||||
if query.is_empty() {
|
||||
let settings = gio::Settings::new(&config::APP_ID);
|
||||
|
||||
let programs = vec![
|
||||
Program::deserialize(&settings.string("program1")).unwrap(),
|
||||
Program::deserialize(&settings.string("program2")).unwrap(),
|
||||
Program::deserialize(&settings.string("program3")).unwrap(),
|
||||
];
|
||||
|
||||
for program in &programs {
|
||||
obj.imp()
|
||||
.programs_flow_box
|
||||
.append(&ProgramTile::new(program.to_owned()));
|
||||
}
|
||||
|
||||
obj.imp().programs.replace(programs);
|
||||
}
|
||||
|
||||
obj.imp().query.set(query).unwrap();
|
||||
obj.search("");
|
||||
|
||||
obj
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn edit_button_clicked(&self) {
|
||||
if let Some(highlight) = &*self.imp().highlight.borrow() {
|
||||
match highlight {
|
||||
Tag::Composer(person) | Tag::Performer(person) => {
|
||||
self.navigation().push(&PersonEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(person),
|
||||
));
|
||||
}
|
||||
Tag::Ensemble(ensemble) => {
|
||||
self.navigation().push(&EnsembleEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(ensemble),
|
||||
));
|
||||
}
|
||||
Tag::Instrument(instrument) => self.navigation().push(&InstrumentEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(instrument),
|
||||
)),
|
||||
Tag::Work(work) => self.navigation().push(&WorkEditor::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
Some(work),
|
||||
false,
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn play_button_clicked(&self) {
|
||||
let program = Program::from_query(self.imp().query.get().unwrap().clone());
|
||||
self.player().set_program(program);
|
||||
self.player().play();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn select(&self) {
|
||||
let imp = self.imp();
|
||||
|
||||
if imp.programs_flow_box.is_visible() {
|
||||
if let Some(program) = imp.programs.borrow().first().cloned() {
|
||||
self.player().set_program(program);
|
||||
}
|
||||
} else {
|
||||
let mut new_query = self.imp().query.get().unwrap().clone();
|
||||
|
||||
let query_changed = if let Some(person) = imp.composers.borrow().first().cloned() {
|
||||
new_query.composer = Some(person);
|
||||
true
|
||||
} else if let Some(person) = imp.performers.borrow().first().cloned() {
|
||||
new_query.performer = Some(person);
|
||||
true
|
||||
} else if let Some(ensemble) = imp.ensembles.borrow().first().cloned() {
|
||||
new_query.ensemble = Some(ensemble);
|
||||
true
|
||||
} else if let Some(instrument) = imp.instruments.borrow().first().cloned() {
|
||||
new_query.instrument = Some(instrument);
|
||||
true
|
||||
} else if let Some(work) = imp.works.borrow().first().cloned() {
|
||||
new_query.work = Some(work);
|
||||
true
|
||||
} else if let Some(recording) = imp.recordings.borrow().first().cloned() {
|
||||
let playlist = self.player().recording_to_playlist(&recording);
|
||||
self.player().append_and_play(playlist);
|
||||
false
|
||||
} else if let Some(album) = imp.albums.borrow().first().cloned() {
|
||||
self.show_album(&album);
|
||||
false
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if query_changed {
|
||||
self.navigation().push(&SearchPage::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
&self.player(),
|
||||
new_query,
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn program_selected(&self, tile: >k::FlowBoxChild) {
|
||||
self.player()
|
||||
.set_program(tile.downcast_ref::<ProgramTile>().unwrap().program());
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn tile_selected(&self, tile: >k::FlowBoxChild) {
|
||||
let mut new_query = self.imp().query.get().unwrap().clone();
|
||||
match tile.downcast_ref::<TagTile>().unwrap().tag().clone() {
|
||||
Tag::Composer(person) => new_query.composer = Some(person),
|
||||
Tag::Performer(person) => new_query.performer = Some(person),
|
||||
Tag::Ensemble(ensemble) => new_query.ensemble = Some(ensemble),
|
||||
Tag::Instrument(instrument) => new_query.instrument = Some(instrument),
|
||||
Tag::Work(work) => new_query.work = Some(work),
|
||||
}
|
||||
|
||||
self.navigation().push(&SearchPage::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
&self.player(),
|
||||
new_query,
|
||||
));
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn recording_selected(&self, tile: >k::FlowBoxChild) {
|
||||
let playlist = self
|
||||
.player()
|
||||
.recording_to_playlist(tile.downcast_ref::<RecordingTile>().unwrap().recording());
|
||||
self.player().append_and_play(playlist);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
fn album_selected(&self, tile: >k::FlowBoxChild) {
|
||||
self.show_album(tile.downcast_ref::<AlbumTile>().unwrap().album());
|
||||
}
|
||||
|
||||
fn show_album(&self, album: &Album) {
|
||||
self.navigation().push(&AlbumPage::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
&self.player(),
|
||||
album.to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
fn search(&self, search: &str) {
|
||||
let query = self.imp().query.get().unwrap();
|
||||
|
||||
let imp = self.imp();
|
||||
let results = self.library().search(query, search).unwrap();
|
||||
|
||||
for flowbox in [
|
||||
&imp.composers_flow_box,
|
||||
&imp.performers_flow_box,
|
||||
&imp.ensembles_flow_box,
|
||||
&imp.instruments_flow_box,
|
||||
&imp.works_flow_box,
|
||||
&imp.recordings_flow_box,
|
||||
&imp.albums_flow_box,
|
||||
] {
|
||||
while let Some(widget) = flowbox.first_child() {
|
||||
flowbox.remove(&widget);
|
||||
}
|
||||
}
|
||||
|
||||
// Only show programs initially.
|
||||
imp.programs_flow_box
|
||||
.set_visible(query.is_empty() && search.is_empty());
|
||||
|
||||
imp.header_bar.set_show_title(query.is_empty());
|
||||
imp.header_box.set_visible(!query.is_empty());
|
||||
|
||||
let highlight = if let Some(work) = &query.work {
|
||||
imp.title_label.set_text(&work.name.get());
|
||||
if let Some(composers) = work.composers_string() {
|
||||
imp.subtitle_label.set_text(&composers);
|
||||
imp.subtitle_label.set_visible(true);
|
||||
} else {
|
||||
imp.subtitle_label.set_visible(false);
|
||||
}
|
||||
Some(Tag::Work(work.to_owned()))
|
||||
} else if let Some(person) = &query.composer {
|
||||
imp.title_label.set_text(&person.name.get());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
Some(Tag::Composer(person.to_owned()))
|
||||
} else if let Some(person) = &query.performer {
|
||||
imp.title_label.set_text(&person.name.get());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
Some(Tag::Performer(person.to_owned()))
|
||||
} else if let Some(ensemble) = &query.ensemble {
|
||||
imp.title_label.set_text(&ensemble.name.get());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
Some(Tag::Ensemble(ensemble.to_owned()))
|
||||
} else if let Some(instrument) = &query.instrument {
|
||||
imp.title_label
|
||||
.set_text(&formatx!(gettext("Music for {}"), &instrument.name.get()).unwrap());
|
||||
imp.subtitle_label.set_visible(false);
|
||||
Some(Tag::Instrument(instrument.to_owned()))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(highlight) = &highlight {
|
||||
if !matches!(highlight, Tag::Work(_)) {
|
||||
let mut details = Vec::new();
|
||||
|
||||
match highlight {
|
||||
Tag::Composer(_) => {
|
||||
if let Some(instrument) = &query.instrument {
|
||||
details.push(formatx!(gettext("Works with {}"), instrument).unwrap());
|
||||
}
|
||||
|
||||
if let (Some(person), Some(ensemble)) = (&query.performer, &query.ensemble)
|
||||
{
|
||||
details.push(
|
||||
formatx!(gettext("Performed by {} and {}"), person, ensemble)
|
||||
.unwrap(),
|
||||
);
|
||||
} else if let Some(person) = &query.performer {
|
||||
details.push(formatx!(gettext("Performed by {}"), person).unwrap());
|
||||
} else if let Some(ensemble) = &query.ensemble {
|
||||
details.push(formatx!(gettext("Performed by {}"), ensemble).unwrap());
|
||||
}
|
||||
}
|
||||
Tag::Performer(_) => {
|
||||
if let Some(instrument) = &query.instrument {
|
||||
details.push(formatx!(gettext("Works with {}"), instrument).unwrap());
|
||||
}
|
||||
|
||||
if let Some(ensemble) = &query.ensemble {
|
||||
details.push(formatx!(gettext("Performed with {}"), ensemble).unwrap());
|
||||
}
|
||||
}
|
||||
Tag::Ensemble(_) => {
|
||||
if let Some(instrument) = &query.instrument {
|
||||
details.push(formatx!(gettext("Works with {}"), instrument).unwrap());
|
||||
}
|
||||
}
|
||||
Tag::Instrument(_) => (),
|
||||
// Already covered.
|
||||
Tag::Work(_) => unreachable!(),
|
||||
}
|
||||
|
||||
imp.subtitle_label.set_visible(!details.is_empty());
|
||||
imp.subtitle_label.set_text(&details.join(", "));
|
||||
}
|
||||
}
|
||||
|
||||
imp.highlight.replace(highlight);
|
||||
|
||||
if results.is_empty() {
|
||||
imp.stack.set_visible_child_name("empty");
|
||||
} else {
|
||||
imp.stack.set_visible_child_name("results");
|
||||
|
||||
imp.composers_flow_box
|
||||
.set_visible(!results.composers.is_empty());
|
||||
imp.performers_flow_box
|
||||
.set_visible(!results.performers.is_empty());
|
||||
imp.ensembles_flow_box
|
||||
.set_visible(!results.ensembles.is_empty());
|
||||
imp.instruments_flow_box
|
||||
.set_visible(!results.instruments.is_empty());
|
||||
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
|
||||
.append(&TagTile::new(Tag::Composer(composer.clone())));
|
||||
}
|
||||
|
||||
for performer in &results.performers {
|
||||
imp.performers_flow_box
|
||||
.append(&TagTile::new(Tag::Performer(performer.clone())));
|
||||
}
|
||||
|
||||
for ensemble in &results.ensembles {
|
||||
imp.ensembles_flow_box
|
||||
.append(&TagTile::new(Tag::Ensemble(ensemble.clone())));
|
||||
}
|
||||
|
||||
for instrument in &results.instruments {
|
||||
imp.instruments_flow_box
|
||||
.append(&TagTile::new(Tag::Instrument(instrument.clone())));
|
||||
}
|
||||
|
||||
for work in &results.works {
|
||||
imp.works_flow_box
|
||||
.append(&TagTile::new(Tag::Work(work.clone())));
|
||||
}
|
||||
|
||||
for recording in &results.recordings {
|
||||
imp.recordings_flow_box.append(&RecordingTile::new(
|
||||
&self.navigation(),
|
||||
&self.library(),
|
||||
recording,
|
||||
));
|
||||
}
|
||||
|
||||
for album in &results.albums {
|
||||
imp.albums_flow_box.append(&AlbumTile::new(album));
|
||||
}
|
||||
|
||||
imp.composers.replace(results.composers);
|
||||
imp.performers.replace(results.performers);
|
||||
imp.ensembles.replace(results.ensembles);
|
||||
imp.instruments.replace(results.instruments);
|
||||
imp.works.replace(results.works);
|
||||
imp.recordings.replace(results.recordings);
|
||||
imp.albums.replace(results.albums);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,9 +4,15 @@ use adw::subclass::prelude::*;
|
|||
use gtk::{gio, glib, glib::clone, prelude::*};
|
||||
|
||||
use crate::{
|
||||
config, editor::tracks::TracksEditor, home_page::HomePage, library::Library,
|
||||
library_manager::LibraryManager, player::Player, player_bar::PlayerBar,
|
||||
playlist_page::PlaylistPage, welcome_page::WelcomePage,
|
||||
config,
|
||||
editor::tracks::TracksEditor,
|
||||
library::{Library, LibraryQuery},
|
||||
library_manager::LibraryManager,
|
||||
player::Player,
|
||||
player_bar::PlayerBar,
|
||||
playlist_page::PlaylistPage,
|
||||
search_page::SearchPage,
|
||||
welcome_page::WelcomePage,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
|
|
@ -189,7 +195,13 @@ impl Window {
|
|||
self.imp().player.set_library(&library);
|
||||
|
||||
let navigation = self.imp().navigation_view.get();
|
||||
navigation.replace(&[HomePage::new(&navigation, &library, &self.imp().player).into()]);
|
||||
navigation.replace(&[SearchPage::new(
|
||||
&navigation,
|
||||
&library,
|
||||
&self.imp().player,
|
||||
LibraryQuery::default(),
|
||||
)
|
||||
.into()]);
|
||||
|
||||
self.imp().library.replace(Some(library));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue