From f4c36de342535737399098334f43fd6a9c09a32b Mon Sep 17 00:00:00 2001 From: Elias Projahn Date: Sun, 9 Jun 2024 16:26:37 +0200 Subject: [PATCH] program: Initialize from settings --- data/de.johrpan.musicus.gschema.xml | 15 ++++ src/home_page.rs | 32 +++----- src/main.rs | 1 + src/program.rs | 119 ++++++++++++++++++++++++++++ src/program_tile.rs | 75 +++++++++--------- 5 files changed, 183 insertions(+), 59 deletions(-) create mode 100644 src/program.rs diff --git a/data/de.johrpan.musicus.gschema.xml b/data/de.johrpan.musicus.gschema.xml index fd46ba7..97e8741 100644 --- a/data/de.johrpan.musicus.gschema.xml +++ b/data/de.johrpan.musicus.gschema.xml @@ -17,5 +17,20 @@ '' Path to the music library + + + '{"title":"Just play some music","description":"Randomly select some music. Customize programs using the button in the top right.","design":"Program1","prefer_recently_added":0.0,"prefer_least_recently_played":0.0,"play_full_recordings":true}' + Default settings for program 1 + + + + '{"title":"What\'s new?","description":"Recordings that you recently added to your music library.","design":"Program2","prefer_recently_added":1.0,"prefer_least_recently_played":0.0,"play_full_recordings":true}' + Default settings for program 2 + + + + '{"title":"A long time ago","description":"Works that you haven\'t listend to for a long time.","design":"Program3","prefer_recently_added":-1.0,"prefer_least_recently_played":1.0,"play_full_recordings":false}' + Default settings for program 3 + diff --git a/src/home_page.rs b/src/home_page.rs index 621e542..c10567c 100644 --- a/src/home_page.rs +++ b/src/home_page.rs @@ -5,7 +5,8 @@ use crate::{ library::{LibraryQuery, MusicusLibrary}, player::MusicusPlayer, playlist_item::PlaylistItem, - program_tile::{MusicusProgramTile, Program, ProgramTileDesign}, + program::Program, + program_tile::MusicusProgramTile, recording_tile::MusicusRecordingTile, search_entry::MusicusSearchEntry, search_tag::Tag, @@ -14,6 +15,7 @@ use crate::{ use adw::subclass::{navigation_page::NavigationPageImpl, prelude::*}; use gtk::{ + gio, glib::{self, clone, Properties}, prelude::*, }; @@ -107,29 +109,19 @@ mod imp { .sync_create() .build(); - self.programs_flow_box - .append(&MusicusProgramTile::new(Program { - title: "Just play some music".to_string(), - description: "Randomly select some music. Customize programs using the button in the top right." - .to_string(), - design: Some(ProgramTileDesign::Program1) - }, - )); + let settings = gio::Settings::new("de.johrpan.musicus"); + let program1 = Program::deserialize(&settings.string("program1")).unwrap(); + let program2 = Program::deserialize(&settings.string("program2")).unwrap(); + let program3 = Program::deserialize(&settings.string("program3")).unwrap(); self.programs_flow_box - .append(&MusicusProgramTile::new(Program { - title: "What's new?".to_string(), - description: "Recordings that you recently added to your music library." - .to_string(), - design: Some(ProgramTileDesign::Program2), - })); + .append(&MusicusProgramTile::new(program1)); self.programs_flow_box - .append(&MusicusProgramTile::new(Program { - title: "A long time ago".to_string(), - description: "Works that you haven't listend to for a long time.".to_string(), - design: Some(ProgramTileDesign::Program3), - })); + .append(&MusicusProgramTile::new(program2)); + + self.programs_flow_box + .append(&MusicusProgramTile::new(program3)); self.obj().query(&LibraryQuery::default()); } diff --git a/src/main.rs b/src/main.rs index 8f416f6..6806c37 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ mod player_bar; mod playlist_item; mod playlist_page; mod playlist_tile; +mod program; mod program_tile; mod recording_tile; mod search_entry; diff --git a/src/program.rs b/src/program.rs new file mode 100644 index 0000000..8b3555c --- /dev/null +++ b/src/program.rs @@ -0,0 +1,119 @@ +use std::cell::{Cell, RefCell}; + +use anyhow::Result; +use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*}; +use serde::{Deserialize, Serialize}; + +use crate::library::LibraryQuery; + +mod imp { + use super::*; + + #[derive(Properties, Serialize, Deserialize, Default)] + #[properties(wrapper_type = super::Program)] + pub struct Program { + #[property(get, set)] + pub title: RefCell>, + + #[property(get, set)] + pub description: RefCell>, + + #[property(get, set, builder(ProgramDesign::default()))] + pub design: Cell, + + #[property(get, set)] + pub composer_id: RefCell>, + + #[property(get, set)] + pub performer_id: RefCell>, + + #[property(get, set)] + pub ensemble_id: RefCell>, + + #[property(get, set)] + pub work_id: RefCell>, + + #[property(get, set)] + pub album_id: RefCell>, + + #[property(get, set)] + pub prefer_recently_added: Cell, + + #[property(get, set)] + pub prefer_least_recently_played: Cell, + + #[property(get, set)] + pub play_full_recordings: Cell, + } + + #[glib::object_subclass] + impl ObjectSubclass for Program { + const NAME: &'static str = "MusicusProgram"; + type Type = super::Program; + } + + #[glib::derived_properties] + impl ObjectImpl for Program {} +} + +glib::wrapper! { + pub struct Program(ObjectSubclass); +} + +impl Program { + pub fn new(title: &str, description: &str, design: ProgramDesign) -> Self { + glib::Object::builder() + .property("title", title) + .property("description", description) + .property("design", design) + .build() + } + + pub fn from_query(query: LibraryQuery) -> Self { + glib::Object::builder() + .property("composer-id", query.composer.map(|p| p.person_id)) + .property("performer-id", query.performer.map(|p| p.person_id)) + .property("ensemble-id", query.ensemble.map(|e| e.ensemble_id)) + .property("prefer-recently-added", 0.25) + .property("prefer-least-recently-played", 0.5) + .property("play-full-recordings", true) + .build() + } + + pub fn deserialize(input: &str) -> Result { + let data: imp::Program = serde_json::from_str(input)?; + + let obj = glib::Object::builder() + .property("title", &*data.title.borrow()) + .property("description", &*data.description.borrow()) + .property("design", data.design.get()) + .property("prefer-recently-added", data.prefer_recently_added.get()) + .property("prefer-least-recently-played", data.prefer_least_recently_played.get()) + .property("play-full-recordings", data.play_full_recordings.get()) + .build(); + + Ok(obj) + } + + pub fn serialize(&self) -> String { + serde_json::to_string(self.imp()).unwrap() + } +} + +#[derive(glib::Enum, Serialize, Deserialize, Eq, PartialEq, Clone, Copy, Debug)] +#[enum_type(name = "MusicusProgramDesign")] +pub enum ProgramDesign { + Generic, + Program1, + Program2, + Program3, + Program4, + Program5, + Program6, +} + +impl Default for ProgramDesign { + fn default() -> Self { + Self::Generic + } +} diff --git a/src/program_tile.rs b/src/program_tile.rs index 967025b..9ed502b 100644 --- a/src/program_tile.rs +++ b/src/program_tile.rs @@ -1,21 +1,29 @@ -use adw::prelude::WidgetExt; -use gtk::{glib, subclass::prelude::*}; use std::cell::OnceCell; +use gtk::{ + glib::{self, Properties}, + prelude::*, + subclass::prelude::*, +}; + +use crate::program::{Program, ProgramDesign}; + mod imp { use super::*; - #[derive(Debug, Default, gtk::CompositeTemplate)] + #[derive(Properties, Debug, Default, gtk::CompositeTemplate)] + #[properties(wrapper_type = super::MusicusProgramTile)] #[template(file = "data/ui/program_tile.blp")] pub struct MusicusProgramTile { + #[property(get, construct_only)] + pub program: OnceCell, + #[template_child] pub edit_button: TemplateChild, #[template_child] pub title_label: TemplateChild, #[template_child] pub description_label: TemplateChild, - - pub program: OnceCell, } #[glib::object_subclass] @@ -33,7 +41,9 @@ mod imp { } } + #[glib::derived_properties] impl ObjectImpl for MusicusProgramTile {} + impl WidgetImpl for MusicusProgramTile {} impl FlowBoxChildImpl for MusicusProgramTile {} } @@ -45,46 +55,33 @@ glib::wrapper! { impl MusicusProgramTile { pub fn new(program: Program) -> Self { - let obj: Self = glib::Object::new(); + let obj: Self = glib::Object::builder() + .property("program", &program) + .build(); + let imp = obj.imp(); - if let Some(design) = program.design { + if program.design() != ProgramDesign::Generic { obj.add_css_class("highlight"); - obj.add_css_class(match design { - ProgramTileDesign::Program1 => "program1", - ProgramTileDesign::Program2 => "program2", - ProgramTileDesign::Program3 => "program3", - ProgramTileDesign::Program4 => "program4", - ProgramTileDesign::Program5 => "program5", - ProgramTileDesign::Program6 => "program6", + obj.add_css_class(match program.design() { + ProgramDesign::Generic => "generic", + ProgramDesign::Program1 => "program1", + ProgramDesign::Program2 => "program2", + ProgramDesign::Program3 => "program3", + ProgramDesign::Program4 => "program4", + ProgramDesign::Program5 => "program5", + ProgramDesign::Program6 => "program6", }) } - - imp.title_label.set_label(&program.title); - imp.description_label.set_label(&program.description); - imp.program.set(program).unwrap(); + + if let Some(title) = program.title() { + imp.title_label.set_label(&title); + } + + if let Some(description) = program.description() { + imp.description_label.set_label(&description); + } obj } - - pub fn program(&self) -> &Program { - self.imp().program.get().unwrap() - } -} - -#[derive(Debug, Default)] -pub struct Program { - pub title: String, - pub description: String, - pub design: Option, -} - -#[derive(Clone, Copy, Debug)] -pub enum ProgramTileDesign { - Program1, - Program2, - Program3, - Program4, - Program5, - Program6, }