mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 11:47:25 +01:00
Inital library manager UI
This commit is contained in:
parent
38638d6fcd
commit
f0135cd415
8 changed files with 1528 additions and 486 deletions
1047
Cargo.lock
generated
1047
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -12,10 +12,11 @@ diesel_migrations = "2.2"
|
||||||
fragile = "2"
|
fragile = "2"
|
||||||
gettext-rs = { version = "0.7", features = ["gettext-system"] }
|
gettext-rs = { version = "0.7", features = ["gettext-system"] }
|
||||||
gstreamer-play = "0.23"
|
gstreamer-play = "0.23"
|
||||||
gtk = { package = "gtk4", version = "0.9", features = ["v4_12", "blueprint"] }
|
gtk = { package = "gtk4", version = "0.9", features = ["v4_18", "blueprint"] }
|
||||||
|
glib = { version = "0.20", features = ["v2_84"] }
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mpris-player = "0.6"
|
mpris-server = "0.8"
|
||||||
once_cell = "1"
|
once_cell = "1"
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
|
||||||
|
|
@ -9,48 +9,286 @@ template $MusicusLibraryManager : Adw.NavigationPage {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {}
|
Adw.HeaderBar {}
|
||||||
|
|
||||||
|
Gtk.ScrolledWindow {
|
||||||
|
Adw.Clamp {
|
||||||
Gtk.Box {
|
Gtk.Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
spacing: 12;
|
margin-bottom: 24;
|
||||||
|
margin-start: 12;
|
||||||
|
margin-end: 12;
|
||||||
|
|
||||||
Gtk.Button {
|
Gtk.Label {
|
||||||
label: _("Add person");
|
label: _("Overview");
|
||||||
clicked => $add_person() swapped;
|
xalign: 0;
|
||||||
|
margin-top: 24;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"heading"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Gtk.ListBox {
|
||||||
label: _("Add role");
|
selection-mode: none;
|
||||||
clicked => $add_role() swapped;
|
margin-top: 12;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"boxed-list-separate"
|
||||||
|
]
|
||||||
|
|
||||||
|
Adw.ActionRow library_path_row {
|
||||||
|
title: _("Library path");
|
||||||
|
activatable: true;
|
||||||
|
activated => $open_library() swapped;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"property"
|
||||||
|
]
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "document-edit-symbolic";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Adw.ButtonRow {
|
||||||
label: _("Add instrument");
|
title: _("Import from archive");
|
||||||
clicked => $add_instrument() swapped;
|
end-icon-name: "go-next-symbolic";
|
||||||
|
activated => $import_archive() swapped;
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Adw.ButtonRow {
|
||||||
label: _("Add work");
|
title: _("Export to archive");
|
||||||
clicked => $add_work() swapped;
|
end-icon-name: "go-next-symbolic";
|
||||||
|
activated => $export_archive() swapped;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Gtk.Label {
|
||||||
label: _("Add ensemble");
|
label: _("Contents");
|
||||||
clicked => $add_ensemble() swapped;
|
xalign: 0;
|
||||||
|
margin-top: 24;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"heading"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Gtk.ListBox {
|
||||||
label: _("Add recording");
|
selection-mode: none;
|
||||||
clicked => $add_recording() swapped;
|
margin-top: 12;
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"boxed-list"
|
||||||
|
]
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Persons");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_persons() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_persons_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Gtk.Image {
|
||||||
label: _("Add album");
|
icon-name: "go-next-symbolic";
|
||||||
clicked => $add_album() swapped;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Gtk.Button {
|
Adw.ActionRow {
|
||||||
label: _("Add medium");
|
title: _("Roles");
|
||||||
clicked => $add_medium() swapped;
|
activatable: true;
|
||||||
|
activated => $show_roles() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_roles_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Instruments");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_instruments() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_instruments_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Works");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_works() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_works_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Ensembles");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_ensembles() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_ensembles_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Recordings");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_recordings() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_recordings_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Tracks");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_tracks() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_tracks_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Mediums");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_mediums() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_mediums_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Adw.ActionRow {
|
||||||
|
title: _("Albums");
|
||||||
|
activatable: true;
|
||||||
|
activated => $show_albums() swapped;
|
||||||
|
|
||||||
|
[suffix]
|
||||||
|
Gtk.Box {
|
||||||
|
spacing: 6;
|
||||||
|
|
||||||
|
Gtk.Label n_albums_label {
|
||||||
|
label: "0";
|
||||||
|
|
||||||
|
styles [
|
||||||
|
"numeric"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Gtk.Image {
|
||||||
|
icon-name: "go-next-symbolic";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
111
src/library.rs
111
src/library.rs
|
|
@ -1,8 +1,9 @@
|
||||||
use std::{
|
use crate::{
|
||||||
cell::{OnceCell, RefCell},
|
db::{self, models::*, schema::*, tables, TranslatedString},
|
||||||
path::{Path, PathBuf},
|
program::Program,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use adw::gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use diesel::{
|
use diesel::{
|
||||||
|
|
@ -12,11 +13,10 @@ use diesel::{
|
||||||
sql_types::BigInt,
|
sql_types::BigInt,
|
||||||
QueryDsl, SqliteConnection,
|
QueryDsl, SqliteConnection,
|
||||||
};
|
};
|
||||||
use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
|
|
||||||
|
|
||||||
use crate::{
|
use std::{
|
||||||
db::{self, models::*, schema::*, tables, TranslatedString},
|
cell::{OnceCell, RefCell},
|
||||||
program::Program,
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
diesel::define_sql_function! {
|
diesel::define_sql_function! {
|
||||||
|
|
@ -537,6 +537,15 @@ impl MusicusLibrary {
|
||||||
Ok(persons)
|
Ok(persons)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_persons(&self) -> Result<Vec<Person>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let persons = persons::table.order(persons::name).load(connection)?;
|
||||||
|
|
||||||
|
Ok(persons)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_roles(&self, search: &str) -> Result<Vec<Role>> {
|
pub fn search_roles(&self, search: &str) -> Result<Vec<Role>> {
|
||||||
let search = format!("%{}%", search);
|
let search = format!("%{}%", search);
|
||||||
let mut binding = self.imp().connection.borrow_mut();
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
|
@ -551,6 +560,15 @@ impl MusicusLibrary {
|
||||||
Ok(roles)
|
Ok(roles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_roles(&self) -> Result<Vec<Role>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let roles = roles::table.order(roles::name).load(connection)?;
|
||||||
|
|
||||||
|
Ok(roles)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_instruments(&self, search: &str) -> Result<Vec<Instrument>> {
|
pub fn search_instruments(&self, search: &str) -> Result<Vec<Instrument>> {
|
||||||
let search = format!("%{}%", search);
|
let search = format!("%{}%", search);
|
||||||
let mut binding = self.imp().connection.borrow_mut();
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
|
@ -565,6 +583,17 @@ impl MusicusLibrary {
|
||||||
Ok(instruments)
|
Ok(instruments)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_instruments(&self) -> Result<Vec<Instrument>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let instruments = instruments::table
|
||||||
|
.order(instruments::name)
|
||||||
|
.load(connection)?;
|
||||||
|
|
||||||
|
Ok(instruments)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_works(&self, composer: &Person, search: &str) -> Result<Vec<Work>> {
|
pub fn search_works(&self, composer: &Person, search: &str) -> Result<Vec<Work>> {
|
||||||
let search = format!("%{}%", search);
|
let search = format!("%{}%", search);
|
||||||
let mut binding = self.imp().connection.borrow_mut();
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
|
@ -588,6 +617,20 @@ impl MusicusLibrary {
|
||||||
Ok(works)
|
Ok(works)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_works(&self) -> Result<Vec<Work>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let works = works::table
|
||||||
|
.order(works::name)
|
||||||
|
.load::<tables::Work>(connection)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|w| Work::from_table(w, connection))
|
||||||
|
.collect::<Result<Vec<Work>>>()?;
|
||||||
|
|
||||||
|
Ok(works)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn search_ensembles(&self, search: &str) -> Result<Vec<Ensemble>> {
|
pub fn search_ensembles(&self, search: &str) -> Result<Vec<Ensemble>> {
|
||||||
let search = format!("%{}%", search);
|
let search = format!("%{}%", search);
|
||||||
let mut binding = self.imp().connection.borrow_mut();
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
|
@ -611,6 +654,60 @@ impl MusicusLibrary {
|
||||||
Ok(ensembles)
|
Ok(ensembles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn all_ensembles(&self) -> Result<Vec<Ensemble>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let ensembles = ensembles::table
|
||||||
|
.order(ensembles::name)
|
||||||
|
.load::<tables::Ensemble>(connection)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| Ensemble::from_table(e, connection))
|
||||||
|
.collect::<Result<Vec<Ensemble>>>()?;
|
||||||
|
|
||||||
|
Ok(ensembles)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_recordings(&self) -> Result<Vec<Recording>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let recordings = recordings::table
|
||||||
|
.load::<tables::Recording>(connection)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| Recording::from_table(e, connection))
|
||||||
|
.collect::<Result<Vec<Recording>>>()?;
|
||||||
|
|
||||||
|
Ok(recordings)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_tracks(&self) -> Result<Vec<Track>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let tracks = tracks::table
|
||||||
|
.load::<tables::Track>(connection)?
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| Track::from_table(e, connection))
|
||||||
|
.collect::<Result<Vec<Track>>>()?;
|
||||||
|
|
||||||
|
Ok(tracks)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_mediums(&self) -> Result<Vec<tables::Medium>> {
|
||||||
|
// TODO
|
||||||
|
Ok(vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_albums(&self) -> Result<Vec<Album>> {
|
||||||
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
||||||
|
let albums = albums::table.load::<Album>(connection)?;
|
||||||
|
|
||||||
|
Ok(albums)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn composer_default_role(&self) -> Result<Role> {
|
pub fn composer_default_role(&self) -> Result<Role> {
|
||||||
let mut binding = self.imp().connection.borrow_mut();
|
let mut binding = self.imp().connection.borrow_mut();
|
||||||
let connection = &mut *binding.as_mut().unwrap();
|
let connection = &mut *binding.as_mut().unwrap();
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,20 @@
|
||||||
use adw::subclass::prelude::*;
|
|
||||||
use gtk::glib;
|
|
||||||
use std::cell::OnceCell;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
editor::{
|
db::{
|
||||||
ensemble_editor::MusicusEnsembleEditor, instrument_editor::MusicusInstrumentEditor,
|
models::{Album, Ensemble, Instrument, Person, Recording, Role, Track, Work},
|
||||||
person_editor::MusicusPersonEditor, recording_editor::MusicusRecordingEditor,
|
tables::Medium,
|
||||||
role_editor::MusicusRoleEditor, work_editor::MusicusWorkEditor,
|
|
||||||
},
|
},
|
||||||
library::MusicusLibrary,
|
library::MusicusLibrary,
|
||||||
|
window::MusicusWindow,
|
||||||
|
};
|
||||||
|
|
||||||
|
use adw::{prelude::*, subclass::prelude::*};
|
||||||
|
use gettextrs::gettext;
|
||||||
|
use gtk::glib;
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
cell::{OnceCell, RefCell},
|
||||||
|
ffi::OsStr,
|
||||||
|
path::Path,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
|
|
@ -19,6 +25,37 @@ mod imp {
|
||||||
pub struct LibraryManager {
|
pub struct LibraryManager {
|
||||||
pub navigation: OnceCell<adw::NavigationView>,
|
pub navigation: OnceCell<adw::NavigationView>,
|
||||||
pub library: OnceCell<MusicusLibrary>,
|
pub library: OnceCell<MusicusLibrary>,
|
||||||
|
|
||||||
|
pub persons: RefCell<Vec<Person>>,
|
||||||
|
pub roles: RefCell<Vec<Role>>,
|
||||||
|
pub instruments: RefCell<Vec<Instrument>>,
|
||||||
|
pub works: RefCell<Vec<Work>>,
|
||||||
|
pub ensembles: RefCell<Vec<Ensemble>>,
|
||||||
|
pub recordings: RefCell<Vec<Recording>>,
|
||||||
|
pub tracks: RefCell<Vec<Track>>,
|
||||||
|
pub mediums: RefCell<Vec<Medium>>,
|
||||||
|
pub albums: RefCell<Vec<Album>>,
|
||||||
|
|
||||||
|
#[template_child]
|
||||||
|
pub library_path_row: TemplateChild<adw::ActionRow>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_persons_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_roles_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_instruments_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_works_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_ensembles_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_recordings_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_tracks_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_mediums_label: TemplateChild<gtk::Label>,
|
||||||
|
#[template_child]
|
||||||
|
pub n_albums_label: TemplateChild<gtk::Label>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[glib::object_subclass]
|
#[glib::object_subclass]
|
||||||
|
|
@ -39,7 +76,13 @@ mod imp {
|
||||||
|
|
||||||
impl ObjectImpl for LibraryManager {}
|
impl ObjectImpl for LibraryManager {}
|
||||||
impl WidgetImpl for LibraryManager {}
|
impl WidgetImpl for LibraryManager {}
|
||||||
impl NavigationPageImpl for LibraryManager {}
|
|
||||||
|
impl NavigationPageImpl for LibraryManager {
|
||||||
|
fn showing(&self) {
|
||||||
|
self.parent_showing();
|
||||||
|
self.obj().update();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
glib::wrapper! {
|
glib::wrapper! {
|
||||||
|
|
@ -60,99 +103,215 @@ impl LibraryManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn add_person(&self, _: >k::Button) {
|
async fn open_library(&self, _: &adw::ActionRow) {
|
||||||
|
let dialog = gtk::FileDialog::builder()
|
||||||
|
.title(gettext("Select music library folder"))
|
||||||
|
.modal(true)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let root = self.root();
|
||||||
|
let window = root
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|r| r.downcast_ref::<gtk::Window>())
|
||||||
|
.and_then(|w| w.downcast_ref::<MusicusWindow>())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match dialog.select_folder_future(Some(window)).await {
|
||||||
|
Err(err) => {
|
||||||
|
if !err.matches(gtk::DialogError::Dismissed) {
|
||||||
|
log::error!("Folder selection failed: {err}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(folder) => window.set_library_folder(&folder),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn import_archive(&self, _: &adw::ButtonRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn export_archive(&self, _: &adw::ButtonRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_persons(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_roles(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_instruments(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_works(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_ensembles(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_recordings(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_tracks(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_mediums(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
#[template_callback]
|
||||||
|
fn show_albums(&self, _: &adw::ActionRow) {}
|
||||||
|
|
||||||
|
// TODO: Make this async.
|
||||||
|
fn update(&self) {
|
||||||
|
let library = self.imp().library.get().unwrap();
|
||||||
|
|
||||||
|
if let Some(Some(filename)) = Path::new(&library.folder()).file_name().map(OsStr::to_str) {
|
||||||
|
self.imp().library_path_row.set_subtitle(filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
let persons = library.all_persons().unwrap();
|
||||||
self.imp()
|
self.imp()
|
||||||
.navigation
|
.n_persons_label
|
||||||
.get()
|
.set_label(&persons.len().to_string());
|
||||||
.unwrap()
|
self.imp().persons.replace(persons);
|
||||||
.push(&MusicusPersonEditor::new(
|
|
||||||
&self.imp().navigation.get().unwrap(),
|
|
||||||
&self.imp().library.get().unwrap(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
let roles = library.all_roles().unwrap();
|
||||||
fn add_role(&self, _: >k::Button) {
|
self.imp().n_roles_label.set_label(&roles.len().to_string());
|
||||||
|
self.imp().roles.replace(roles);
|
||||||
|
|
||||||
|
let instruments = library.all_instruments().unwrap();
|
||||||
self.imp()
|
self.imp()
|
||||||
.navigation
|
.n_instruments_label
|
||||||
.get()
|
.set_label(&instruments.len().to_string());
|
||||||
.unwrap()
|
self.imp().instruments.replace(instruments);
|
||||||
.push(&MusicusRoleEditor::new(
|
|
||||||
&self.imp().navigation.get().unwrap(),
|
|
||||||
&self.imp().library.get().unwrap(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
let works = library.all_works().unwrap();
|
||||||
fn add_instrument(&self, _: >k::Button) {
|
self.imp().n_works_label.set_label(&works.len().to_string());
|
||||||
|
self.imp().works.replace(works);
|
||||||
|
|
||||||
|
let ensembles = library.all_ensembles().unwrap();
|
||||||
self.imp()
|
self.imp()
|
||||||
.navigation
|
.n_ensembles_label
|
||||||
.get()
|
.set_label(&ensembles.len().to_string());
|
||||||
.unwrap()
|
self.imp().ensembles.replace(ensembles);
|
||||||
.push(&MusicusInstrumentEditor::new(
|
|
||||||
&self.imp().navigation.get().unwrap(),
|
|
||||||
&self.imp().library.get().unwrap(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
let recordings = library.all_recordings().unwrap();
|
||||||
fn add_work(&self, _: >k::Button) {
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.navigation
|
.n_recordings_label
|
||||||
.get()
|
.set_label(&recordings.len().to_string());
|
||||||
.unwrap()
|
self.imp().recordings.replace(recordings);
|
||||||
.push(&MusicusWorkEditor::new(
|
|
||||||
&self.imp().navigation.get().unwrap(),
|
|
||||||
&self.imp().library.get().unwrap(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
let tracks = library.all_tracks().unwrap();
|
||||||
fn add_ensemble(&self, _: >k::Button) {
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.navigation
|
.n_tracks_label
|
||||||
.get()
|
.set_label(&tracks.len().to_string());
|
||||||
.unwrap()
|
self.imp().tracks.replace(tracks);
|
||||||
.push(&MusicusEnsembleEditor::new(
|
|
||||||
&self.imp().navigation.get().unwrap(),
|
|
||||||
&self.imp().library.get().unwrap(),
|
|
||||||
None,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
let mediums = library.all_mediums().unwrap();
|
||||||
fn add_recording(&self, _: >k::Button) {
|
|
||||||
self.imp()
|
self.imp()
|
||||||
.navigation
|
.n_mediums_label
|
||||||
.get()
|
.set_label(&mediums.len().to_string());
|
||||||
.unwrap()
|
self.imp().mediums.replace(mediums);
|
||||||
.push(&MusicusRecordingEditor::new(
|
|
||||||
&self.imp().navigation.get().unwrap(),
|
let albums = library.all_albums().unwrap();
|
||||||
&self.imp().library.get().unwrap(),
|
self.imp()
|
||||||
None,
|
.n_albums_label
|
||||||
));
|
.set_label(&albums.len().to_string());
|
||||||
|
self.imp().albums.replace(albums);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[template_callback]
|
// #[template_callback]
|
||||||
fn add_medium(&self, _: >k::Button) {
|
// fn add_person(&self, _: >k::Button) {
|
||||||
todo!("Medium import");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[template_callback]
|
|
||||||
fn add_album(&self, _: >k::Button) {
|
|
||||||
todo!("Album editor");
|
|
||||||
// self.imp()
|
// self.imp()
|
||||||
// .navigation
|
// .navigation
|
||||||
// .get()
|
// .get()
|
||||||
// .unwrap()
|
// .unwrap()
|
||||||
// .push(&MusicusAlbumEditor::new(
|
// .push(&MusicusPersonEditor::new(
|
||||||
// &self.imp().navigation.get().unwrap(),
|
// &self.imp().navigation.get().unwrap(),
|
||||||
// &self.imp().library.get().unwrap(),
|
// &self.imp().library.get().unwrap(),
|
||||||
// None,
|
// None,
|
||||||
// ));
|
// ));
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_role(&self, _: >k::Button) {
|
||||||
|
// self.imp()
|
||||||
|
// .navigation
|
||||||
|
// .get()
|
||||||
|
// .unwrap()
|
||||||
|
// .push(&MusicusRoleEditor::new(
|
||||||
|
// &self.imp().navigation.get().unwrap(),
|
||||||
|
// &self.imp().library.get().unwrap(),
|
||||||
|
// None,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_instrument(&self, _: >k::Button) {
|
||||||
|
// self.imp()
|
||||||
|
// .navigation
|
||||||
|
// .get()
|
||||||
|
// .unwrap()
|
||||||
|
// .push(&MusicusInstrumentEditor::new(
|
||||||
|
// &self.imp().navigation.get().unwrap(),
|
||||||
|
// &self.imp().library.get().unwrap(),
|
||||||
|
// None,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_work(&self, _: >k::Button) {
|
||||||
|
// self.imp()
|
||||||
|
// .navigation
|
||||||
|
// .get()
|
||||||
|
// .unwrap()
|
||||||
|
// .push(&MusicusWorkEditor::new(
|
||||||
|
// &self.imp().navigation.get().unwrap(),
|
||||||
|
// &self.imp().library.get().unwrap(),
|
||||||
|
// None,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_ensemble(&self, _: >k::Button) {
|
||||||
|
// self.imp()
|
||||||
|
// .navigation
|
||||||
|
// .get()
|
||||||
|
// .unwrap()
|
||||||
|
// .push(&MusicusEnsembleEditor::new(
|
||||||
|
// &self.imp().navigation.get().unwrap(),
|
||||||
|
// &self.imp().library.get().unwrap(),
|
||||||
|
// None,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_recording(&self, _: >k::Button) {
|
||||||
|
// self.imp()
|
||||||
|
// .navigation
|
||||||
|
// .get()
|
||||||
|
// .unwrap()
|
||||||
|
// .push(&MusicusRecordingEditor::new(
|
||||||
|
// &self.imp().navigation.get().unwrap(),
|
||||||
|
// &self.imp().library.get().unwrap(),
|
||||||
|
// None,
|
||||||
|
// ));
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_medium(&self, _: >k::Button) {
|
||||||
|
// todo!("Medium import");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// #[template_callback]
|
||||||
|
// fn add_album(&self, _: >k::Button) {
|
||||||
|
// todo!("Album editor");
|
||||||
|
// // self.imp()
|
||||||
|
// // .navigation
|
||||||
|
// // .get()
|
||||||
|
// // .unwrap()
|
||||||
|
// // .push(&MusicusAlbumEditor::new(
|
||||||
|
// // &self.imp().navigation.get().unwrap(),
|
||||||
|
// // &self.imp().library.get().unwrap(),
|
||||||
|
// // None,
|
||||||
|
// // ));
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
161
src/player.rs
161
src/player.rs
|
|
@ -1,7 +1,6 @@
|
||||||
use std::{
|
use std::{
|
||||||
cell::{Cell, OnceCell, RefCell},
|
cell::{Cell, OnceCell, RefCell},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
sync::Arc,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use fragile::Fragile;
|
use fragile::Fragile;
|
||||||
|
|
@ -12,7 +11,6 @@ use gtk::{
|
||||||
prelude::*,
|
prelude::*,
|
||||||
subclass::prelude::*,
|
subclass::prelude::*,
|
||||||
};
|
};
|
||||||
use mpris_player::{Metadata, MprisPlayer, PlaybackStatus};
|
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -48,7 +46,7 @@ mod imp {
|
||||||
|
|
||||||
pub play: OnceCell<gstreamer_play::Play>,
|
pub play: OnceCell<gstreamer_play::Play>,
|
||||||
pub play_signal_adapter: OnceCell<gstreamer_play::PlaySignalAdapter>,
|
pub play_signal_adapter: OnceCell<gstreamer_play::PlaySignalAdapter>,
|
||||||
pub mpris: OnceCell<Arc<MprisPlayer>>,
|
pub mpris: OnceCell<mpris_server::Player>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MusicusPlayer {
|
impl MusicusPlayer {
|
||||||
|
|
@ -75,10 +73,22 @@ mod imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
let item = item.downcast::<PlaylistItem>().unwrap();
|
let item = item.downcast::<PlaylistItem>().unwrap();
|
||||||
self.mpris.get().unwrap().set_metadata(Metadata {
|
|
||||||
artist: Some(vec![item.make_title()]),
|
let obj = self.obj().clone();
|
||||||
title: item.make_subtitle(),
|
let item_clone = item.clone();
|
||||||
..Default::default()
|
glib::spawn_future_local(async move {
|
||||||
|
obj.imp()
|
||||||
|
.mpris
|
||||||
|
.get()
|
||||||
|
.unwrap()
|
||||||
|
.set_metadata(
|
||||||
|
mpris_server::Metadata::builder()
|
||||||
|
.artist(vec![item_clone.make_title()])
|
||||||
|
.title(item_clone.make_subtitle().unwrap_or_else(String::new))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
let uri = glib::filename_to_uri(item.path(), None)
|
let uri = glib::filename_to_uri(item.path(), None)
|
||||||
|
|
@ -121,56 +131,13 @@ mod imp {
|
||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
|
let obj = self.obj().clone();
|
||||||
|
glib::spawn_future_local(async move {
|
||||||
|
obj.init_mpris().await;
|
||||||
|
});
|
||||||
|
|
||||||
let play = gstreamer_play::Play::new(None::<gstreamer_play::PlayVideoRenderer>);
|
let play = gstreamer_play::Play::new(None::<gstreamer_play::PlayVideoRenderer>);
|
||||||
|
|
||||||
let mpris = MprisPlayer::new(
|
|
||||||
config::APP_ID.to_owned(),
|
|
||||||
config::NAME.to_owned(),
|
|
||||||
config::APP_ID.to_owned(),
|
|
||||||
);
|
|
||||||
|
|
||||||
mpris.set_can_raise(true);
|
|
||||||
mpris.set_can_play(true);
|
|
||||||
mpris.set_can_pause(true);
|
|
||||||
mpris.set_can_go_previous(true);
|
|
||||||
mpris.set_can_go_next(true);
|
|
||||||
mpris.set_can_seek(false);
|
|
||||||
mpris.set_can_set_fullscreen(false);
|
|
||||||
|
|
||||||
let obj = self.obj();
|
|
||||||
mpris.connect_raise(clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move || obj.emit_by_name::<()>("raise", &[])
|
|
||||||
));
|
|
||||||
mpris.connect_play(clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move || obj.play()
|
|
||||||
));
|
|
||||||
mpris.connect_pause(clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move || obj.pause()
|
|
||||||
));
|
|
||||||
mpris.connect_play_pause(clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move || obj.play_pause()
|
|
||||||
));
|
|
||||||
mpris.connect_previous(clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move || obj.previous()
|
|
||||||
));
|
|
||||||
mpris.connect_next(clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move || obj.next()
|
|
||||||
));
|
|
||||||
|
|
||||||
self.mpris.set(mpris).expect("mpris should not be set");
|
|
||||||
|
|
||||||
let mut config = play.config();
|
let mut config = play.config();
|
||||||
config.set_position_update_interval(250);
|
config.set_position_update_interval(250);
|
||||||
play.set_config(config).unwrap();
|
play.set_config(config).unwrap();
|
||||||
|
|
@ -237,7 +204,11 @@ impl MusicusPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn play_recording(&self, recording: &Recording) {
|
pub fn play_recording(&self, recording: &Recording) {
|
||||||
let tracks = &self.library().unwrap().tracks_for_recording(&recording.recording_id).unwrap();
|
let tracks = &self
|
||||||
|
.library()
|
||||||
|
.unwrap()
|
||||||
|
.tracks_for_recording(&recording.recording_id)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
if tracks.is_empty() {
|
if tracks.is_empty() {
|
||||||
log::warn!("Ignoring recording without tracks being added to the playlist.");
|
log::warn!("Ignoring recording without tracks being added to the playlist.");
|
||||||
|
|
@ -330,20 +301,34 @@ impl MusicusPlayer {
|
||||||
let imp = self.imp();
|
let imp = self.imp();
|
||||||
imp.play.get().unwrap().play();
|
imp.play.get().unwrap().play();
|
||||||
self.set_playing(true);
|
self.set_playing(true);
|
||||||
imp.mpris
|
|
||||||
|
let obj = self.clone();
|
||||||
|
glib::spawn_future_local(async move {
|
||||||
|
obj.imp()
|
||||||
|
.mpris
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_playback_status(PlaybackStatus::Playing);
|
.set_playback_status(mpris_server::PlaybackStatus::Playing)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn pause(&self) {
|
pub fn pause(&self) {
|
||||||
let imp = self.imp();
|
let imp = self.imp();
|
||||||
imp.play.get().unwrap().pause();
|
imp.play.get().unwrap().pause();
|
||||||
self.set_playing(false);
|
self.set_playing(false);
|
||||||
imp.mpris
|
|
||||||
|
let obj = self.clone();
|
||||||
|
glib::spawn_future_local(async move {
|
||||||
|
obj.imp()
|
||||||
|
.mpris
|
||||||
.get()
|
.get()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.set_playback_status(PlaybackStatus::Paused);
|
.set_playback_status(mpris_server::PlaybackStatus::Paused)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn seek_to(&self, time_ms: u64) {
|
pub fn seek_to(&self, time_ms: u64) {
|
||||||
|
|
@ -378,6 +363,62 @@ impl MusicusPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn init_mpris(&self) {
|
||||||
|
let mpris = mpris_server::Player::builder(config::APP_ID)
|
||||||
|
.desktop_entry(config::APP_ID)
|
||||||
|
.can_raise(true)
|
||||||
|
.can_play(true)
|
||||||
|
.can_pause(true)
|
||||||
|
.can_go_previous(true)
|
||||||
|
.can_go_next(true)
|
||||||
|
.build()
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let obj = self.clone();
|
||||||
|
|
||||||
|
mpris.connect_raise(clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
move |_| obj.emit_by_name::<()>("raise", &[])
|
||||||
|
));
|
||||||
|
|
||||||
|
mpris.connect_play(clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
move |_| obj.play()
|
||||||
|
));
|
||||||
|
|
||||||
|
mpris.connect_pause(clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
move |_| obj.pause()
|
||||||
|
));
|
||||||
|
|
||||||
|
mpris.connect_play_pause(clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
move |_| obj.play_pause()
|
||||||
|
));
|
||||||
|
|
||||||
|
mpris.connect_previous(clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
move |_| obj.previous()
|
||||||
|
));
|
||||||
|
|
||||||
|
mpris.connect_next(clone!(
|
||||||
|
#[weak]
|
||||||
|
obj,
|
||||||
|
move |_| obj.next()
|
||||||
|
));
|
||||||
|
|
||||||
|
self.imp()
|
||||||
|
.mpris
|
||||||
|
.set(mpris)
|
||||||
|
.expect("mpris should not be set");
|
||||||
|
}
|
||||||
|
|
||||||
fn generate_items(&self, program: &Program) {
|
fn generate_items(&self, program: &Program) {
|
||||||
if let Some(library) = self.library() {
|
if let Some(library) = self.library() {
|
||||||
// TODO: if program.play_full_recordings() {
|
// TODO: if program.play_full_recordings() {
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ use gtk::{
|
||||||
subclass::prelude::*,
|
subclass::prelude::*,
|
||||||
};
|
};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use std::cell::{Cell, RefCell};
|
use std::cell::{Cell, OnceCell};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -16,7 +16,7 @@ mod imp {
|
||||||
#[template(file = "data/ui/player_bar.blp")]
|
#[template(file = "data/ui/player_bar.blp")]
|
||||||
pub struct PlayerBar {
|
pub struct PlayerBar {
|
||||||
#[property(get, construct_only)]
|
#[property(get, construct_only)]
|
||||||
pub player: RefCell<MusicusPlayer>,
|
pub player: OnceCell<MusicusPlayer>,
|
||||||
|
|
||||||
pub seeking: Cell<bool>,
|
pub seeking: Cell<bool>,
|
||||||
|
|
||||||
|
|
@ -42,7 +42,7 @@ mod imp {
|
||||||
|
|
||||||
impl PlayerBar {
|
impl PlayerBar {
|
||||||
fn update_item(&self) {
|
fn update_item(&self) {
|
||||||
if let Some(item) = self.player.borrow().current_item() {
|
if let Some(item) = self.player.get().unwrap().current_item() {
|
||||||
self.title_label.set_label(&item.make_title());
|
self.title_label.set_label(&item.make_title());
|
||||||
|
|
||||||
if let Some(subtitle) = item.make_subtitle() {
|
if let Some(subtitle) = item.make_subtitle() {
|
||||||
|
|
@ -55,7 +55,7 @@ mod imp {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_time(&self) {
|
fn update_time(&self) {
|
||||||
let player = self.player.borrow();
|
let player = self.player.get().unwrap();
|
||||||
|
|
||||||
let current_time_ms = if self.seeking.get() {
|
let current_time_ms = if self.seeking.get() {
|
||||||
(self.slider.value() * player.duration_ms() as f64) as u64
|
(self.slider.value() * player.duration_ms() as f64) as u64
|
||||||
|
|
@ -106,7 +106,7 @@ mod imp {
|
||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
let player = self.player.borrow();
|
let player = self.player.get().unwrap();
|
||||||
|
|
||||||
player
|
player
|
||||||
.bind_property("playing", &self.play_button.get(), "icon-name")
|
.bind_property("playing", &self.play_button.get(), "icon-name")
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use std::path::Path;
|
|
||||||
|
|
||||||
use adw::subclass::prelude::*;
|
|
||||||
use gtk::{gio, glib, glib::clone, prelude::*};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config, home_page::MusicusHomePage, library::MusicusLibrary, library_manager::LibraryManager,
|
config, home_page::MusicusHomePage, library::MusicusLibrary, library_manager::LibraryManager,
|
||||||
player::MusicusPlayer, player_bar::PlayerBar, playlist_page::MusicusPlaylistPage,
|
player::MusicusPlayer, player_bar::PlayerBar, playlist_page::MusicusPlaylistPage,
|
||||||
welcome_page::MusicusWelcomePage,
|
welcome_page::MusicusWelcomePage,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use adw::subclass::prelude::*;
|
||||||
|
use gtk::{gio, glib, glib::clone, prelude::*};
|
||||||
|
|
||||||
|
use std::{cell::RefCell, path::Path};
|
||||||
|
|
||||||
mod imp {
|
mod imp {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
|
@ -16,6 +16,7 @@ mod imp {
|
||||||
#[template(file = "data/ui/window.blp")]
|
#[template(file = "data/ui/window.blp")]
|
||||||
pub struct MusicusWindow {
|
pub struct MusicusWindow {
|
||||||
pub player: MusicusPlayer,
|
pub player: MusicusPlayer,
|
||||||
|
pub library_manager: RefCell<Option<LibraryManager>>,
|
||||||
|
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub stack: TemplateChild<gtk::Stack>,
|
pub stack: TemplateChild<gtk::Stack>,
|
||||||
|
|
@ -157,7 +158,7 @@ impl MusicusWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[template_callback]
|
#[template_callback]
|
||||||
fn set_library_folder(&self, folder: &gio::File) {
|
pub fn set_library_folder(&self, folder: &gio::File) {
|
||||||
let path = folder.path().unwrap();
|
let path = folder.path().unwrap();
|
||||||
|
|
||||||
let settings = gio::Settings::new(config::APP_ID);
|
let settings = gio::Settings::new(config::APP_ID);
|
||||||
|
|
@ -173,8 +174,16 @@ impl MusicusWindow {
|
||||||
self.imp().player.set_library(&library);
|
self.imp().player.set_library(&library);
|
||||||
|
|
||||||
let navigation = self.imp().navigation_view.get();
|
let navigation = self.imp().navigation_view.get();
|
||||||
|
if let Some(library_manager) = self.imp().library_manager.take() {
|
||||||
|
navigation.remove(&library_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
let library_manager = LibraryManager::new(&navigation, &library);
|
||||||
|
|
||||||
navigation
|
navigation
|
||||||
.replace(&[MusicusHomePage::new(&navigation, &library, &self.imp().player).into()]);
|
.replace(&[MusicusHomePage::new(&navigation, &library, &self.imp().player).into()]);
|
||||||
navigation.add(&LibraryManager::new(&navigation, &library));
|
navigation.add(&library_manager);
|
||||||
|
|
||||||
|
self.imp().library_manager.replace(Some(library_manager));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue