mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-25 20:37:24 +02:00
Add preferences for default program
This commit is contained in:
parent
653d5cd629
commit
fa94d61e1e
11 changed files with 367 additions and 28 deletions
|
|
@ -17,19 +17,39 @@
|
|||
<default>''</default>
|
||||
<summary>Path to the music library</summary>
|
||||
</key>
|
||||
<key name="prefer-least-recently-played" type="i">
|
||||
<default>20</default>
|
||||
<summary>How much recently played items should be penalized (0–100)</summary>
|
||||
</key>
|
||||
<key name="prefer-recently-added" type="i">
|
||||
<default>0</default>
|
||||
<summary>How much recently added items should be preferred (0–100)</summary>
|
||||
</key>
|
||||
<key name="avoid-repeated-composers" type="i">
|
||||
<default>60</default>
|
||||
<summary>For how many minutes a composer should be penalized</summary>
|
||||
</key>
|
||||
<key name="avoid-repeated-instruments" type="i">
|
||||
<default>60</default>
|
||||
<summary>For how many minutes an instrument should be penalized</summary>
|
||||
</key>
|
||||
<key name="play-full-recordings" type="b">
|
||||
<default>true</default>
|
||||
<summary>Whether to play full recordings</summary>
|
||||
</key>
|
||||
<key name="program1" type="s">
|
||||
<!-- Translators: Configuration for the default programs in JSON. Please only translate the values of "title" and "description". -->
|
||||
<default l10n="messages">'{"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.1,"avoid_repeated_composers_seconds":3600,"avoid_repeated_instruments_seconds":3600,"play_full_recordings":true}'</default>
|
||||
<default l10n="messages">'{"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.1,"avoid_repeated_composers":60,"avoid_repeated_instruments":60,"play_full_recordings":true}'</default>
|
||||
<summary>Default settings for program 1</summary>
|
||||
</key>
|
||||
<key name="program2" type="s">
|
||||
<!-- Translators: Configuration for the default programs in JSON. Please only translate the values of "title" and "description". -->
|
||||
<default l10n="messages">'{"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,"avoid_repeated_composers_seconds":3600,"avoid_repeated_instruments_seconds":3600,"play_full_recordings":true}'</default>
|
||||
<default l10n="messages">'{"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,"avoid_repeated_composers":60,"avoid_repeated_instruments":60,"play_full_recordings":true}'</default>
|
||||
<summary>Default settings for program 2</summary>
|
||||
</key>
|
||||
<key name="program3" type="s">
|
||||
<!-- Translators: Configuration for the default programs in JSON. Please only translate the values of "title" and "description". -->
|
||||
<default l10n="messages">'{"title":"A long time ago","description":"Works that you haven\'t listened to for a long time.","design":"Program3","prefer_recently_added":0.0,"prefer_least_recently_played":1.0,"avoid_repeated_composers_seconds":3600,"avoid_repeated_instruments_seconds":3600,"play_full_recordings":true}'</default>
|
||||
<default l10n="messages">'{"title":"A long time ago","description":"Works that you haven\'t listened to for a long time.","design":"Program3","prefer_recently_added":0.0,"prefer_least_recently_played":1.0,"avoid_repeated_composers":60,"avoid_repeated_instruments":60,"play_full_recordings":true}'</default>
|
||||
<summary>Default settings for program 3</summary>
|
||||
</key>
|
||||
</schema>
|
||||
|
|
|
|||
65
data/ui/preferences_dialog.blp
Normal file
65
data/ui/preferences_dialog.blp
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $MusicusPreferencesDialog: Adw.PreferencesDialog {
|
||||
Adw.PreferencesPage {
|
||||
title: _("Playback");
|
||||
|
||||
Adw.PreferencesGroup {
|
||||
title: _("Default program");
|
||||
description: _("These settings apply when you add search results to the playlist.");
|
||||
|
||||
$MusicusSliderRow {
|
||||
title: _("Prefer recordings that haven't been played for a long time");
|
||||
suffix: _("%");
|
||||
|
||||
adjustment: Gtk.Adjustment prefer_least_recently_played_adjustment {
|
||||
lower: 0;
|
||||
upper: 100;
|
||||
step-increment: 1;
|
||||
page-increment: 10;
|
||||
};
|
||||
}
|
||||
|
||||
$MusicusSliderRow {
|
||||
title: _("Prefer recordings that were recently added");
|
||||
suffix: _("%");
|
||||
|
||||
adjustment: Gtk.Adjustment prefer_recently_added_adjustment {
|
||||
lower: 0;
|
||||
upper: 100;
|
||||
step-increment: 1;
|
||||
page-increment: 10;
|
||||
};
|
||||
}
|
||||
|
||||
$MusicusSliderRow {
|
||||
title: _("Avoid repeating composers");
|
||||
suffix: _(" min");
|
||||
|
||||
adjustment: Gtk.Adjustment avoid_repeated_composers_adjustment {
|
||||
lower: 0;
|
||||
upper: 120;
|
||||
step-increment: 10;
|
||||
page-increment: 30;
|
||||
};
|
||||
}
|
||||
|
||||
$MusicusSliderRow {
|
||||
title: _("Avoid repeating instruments");
|
||||
suffix: _(" min");
|
||||
|
||||
adjustment: Gtk.Adjustment avoid_repeated_instruments_adjustment {
|
||||
lower: 0;
|
||||
upper: 120;
|
||||
step-increment: 10;
|
||||
page-increment: 30;
|
||||
};
|
||||
}
|
||||
|
||||
Adw.SwitchRow play_full_recordings_row {
|
||||
title: _("Play full recordings");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -269,7 +269,7 @@ menu primary_menu {
|
|||
|
||||
item {
|
||||
label: _("_Preferences");
|
||||
action: "app.preferences";
|
||||
action: "win.preferences";
|
||||
}
|
||||
|
||||
item {
|
||||
|
|
|
|||
41
data/ui/slider_row.blp
Normal file
41
data/ui/slider_row.blp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
using Gtk 4.0;
|
||||
using Adw 1;
|
||||
|
||||
template $MusicusSliderRow: Adw.PreferencesRow {
|
||||
activatable: false;
|
||||
|
||||
Gtk.Box {
|
||||
orientation: vertical;
|
||||
spacing: 12;
|
||||
margin-top: 12;
|
||||
margin-bottom: 12;
|
||||
margin-start: 12;
|
||||
margin-end: 12;
|
||||
|
||||
Gtk.Box {
|
||||
spacing: 12;
|
||||
|
||||
Gtk.Label {
|
||||
label: bind template.title;
|
||||
wrap: true;
|
||||
xalign: 0.0;
|
||||
hexpand: true;
|
||||
}
|
||||
|
||||
Gtk.Label value_label {
|
||||
xalign: 1.0;
|
||||
valign: center;
|
||||
|
||||
styles [
|
||||
"numeric",
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Gtk.Scale {
|
||||
adjustment: bind template.adjustment;
|
||||
hexpand: true;
|
||||
valign: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ template $MusicusWelcomePage : Adw.NavigationPage {
|
|||
menu primary_menu {
|
||||
item {
|
||||
label: _("_Preferences");
|
||||
action: "app.preferences";
|
||||
action: "win.preferences";
|
||||
}
|
||||
item {
|
||||
label: _("_About Musicus");
|
||||
|
|
|
|||
|
|
@ -629,14 +629,14 @@ impl Library {
|
|||
(
|
||||
UNIXEPOCH('now', 'localtime') - UNIXEPOCH(instruments.last_played_at)
|
||||
) * 1.0 / ")
|
||||
.bind::<sql_types::Integer, _>(program.avoid_repeated_instruments_seconds())
|
||||
.bind::<sql_types::Integer, _>(program.avoid_repeated_instruments())
|
||||
.sql(",
|
||||
1.0
|
||||
),
|
||||
IFNULL(
|
||||
(
|
||||
UNIXEPOCH('now', 'localtime') - UNIXEPOCH(persons.last_played_at)
|
||||
) * 1.0 / ").bind::<sql_types::Integer, _>(program.avoid_repeated_composers_seconds()).sql(",
|
||||
) * 1.0 / ").bind::<sql_types::Integer, _>(program.avoid_repeated_composers()).sql(",
|
||||
1.0
|
||||
),
|
||||
1.0
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ mod player_bar;
|
|||
mod playlist_item;
|
||||
mod playlist_page;
|
||||
mod playlist_tile;
|
||||
mod preferences_dialog;
|
||||
mod process;
|
||||
mod process_manager;
|
||||
mod process_row;
|
||||
|
|
@ -20,6 +21,7 @@ mod recording_tile;
|
|||
mod search_page;
|
||||
mod search_tag;
|
||||
mod selector;
|
||||
mod slider_row;
|
||||
mod tag_tile;
|
||||
mod util;
|
||||
mod welcome_page;
|
||||
|
|
|
|||
105
src/preferences_dialog.rs
Normal file
105
src/preferences_dialog.rs
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
use adw::{prelude::AdwDialogExt, subclass::prelude::*};
|
||||
use gtk::{gio, glib, prelude::*};
|
||||
|
||||
use crate::{config, slider_row::SliderRow};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
#[template(file = "data/ui/preferences_dialog.blp")]
|
||||
pub struct PreferencesDialog {
|
||||
#[template_child]
|
||||
pub prefer_least_recently_played_adjustment: TemplateChild<gtk::Adjustment>,
|
||||
#[template_child]
|
||||
pub prefer_recently_added_adjustment: TemplateChild<gtk::Adjustment>,
|
||||
#[template_child]
|
||||
pub avoid_repeated_composers_adjustment: TemplateChild<gtk::Adjustment>,
|
||||
#[template_child]
|
||||
pub avoid_repeated_instruments_adjustment: TemplateChild<gtk::Adjustment>,
|
||||
#[template_child]
|
||||
pub play_full_recordings_row: TemplateChild<adw::SwitchRow>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for PreferencesDialog {
|
||||
const NAME: &'static str = "MusicusPreferencesDialog";
|
||||
type Type = super::PreferencesDialog;
|
||||
type ParentType = adw::PreferencesDialog;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.bind_template();
|
||||
klass.bind_template_instance_callbacks();
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
SliderRow::static_type();
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for PreferencesDialog {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let settings = gio::Settings::new(config::APP_ID);
|
||||
|
||||
settings
|
||||
.bind(
|
||||
"prefer-least-recently-played",
|
||||
&*self.prefer_least_recently_played_adjustment,
|
||||
"value",
|
||||
)
|
||||
.build();
|
||||
|
||||
settings
|
||||
.bind(
|
||||
"prefer-recently-added",
|
||||
&*self.prefer_recently_added_adjustment,
|
||||
"value",
|
||||
)
|
||||
.build();
|
||||
|
||||
settings
|
||||
.bind(
|
||||
"avoid-repeated-composers",
|
||||
&*self.avoid_repeated_composers_adjustment,
|
||||
"value",
|
||||
)
|
||||
.build();
|
||||
|
||||
settings
|
||||
.bind(
|
||||
"avoid-repeated-instruments",
|
||||
&*self.avoid_repeated_instruments_adjustment,
|
||||
"value",
|
||||
)
|
||||
.build();
|
||||
|
||||
settings
|
||||
.bind(
|
||||
"play-full-recordings",
|
||||
&*self.play_full_recordings_row,
|
||||
"active",
|
||||
)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for PreferencesDialog {}
|
||||
impl AdwDialogImpl for PreferencesDialog {}
|
||||
impl PreferencesDialogImpl for PreferencesDialog {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct PreferencesDialog(ObjectSubclass<imp::PreferencesDialog>)
|
||||
@extends gtk::Widget, adw::Dialog, adw::PreferencesDialog;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl PreferencesDialog {
|
||||
pub fn show(parent: &impl IsA<gtk::Widget>) {
|
||||
let obj: Self = glib::Object::new();
|
||||
obj.present(Some(parent));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use anyhow::Result;
|
||||
use gtk::{glib, glib::Properties, prelude::*, subclass::prelude::*};
|
||||
use gtk::{gio, glib, glib::Properties, prelude::*, subclass::prelude::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::library::LibraryQuery;
|
||||
use crate::{config, library::LibraryQuery};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
|
@ -47,10 +47,10 @@ mod imp {
|
|||
pub prefer_least_recently_played: Cell<f64>,
|
||||
|
||||
#[property(get, set)]
|
||||
pub avoid_repeated_composers_seconds: Cell<i32>,
|
||||
pub avoid_repeated_composers: Cell<i32>,
|
||||
|
||||
#[property(get, set)]
|
||||
pub avoid_repeated_instruments_seconds: Cell<i32>,
|
||||
pub avoid_repeated_instruments: Cell<i32>,
|
||||
|
||||
#[property(get, set)]
|
||||
pub play_full_recordings: Cell<bool>,
|
||||
|
|
@ -80,6 +80,8 @@ impl Program {
|
|||
}
|
||||
|
||||
pub fn from_query(query: LibraryQuery) -> Self {
|
||||
let settings = gio::Settings::new(&config::APP_ID);
|
||||
|
||||
glib::Object::builder()
|
||||
.property(
|
||||
"composer-id",
|
||||
|
|
@ -92,25 +94,34 @@ impl Program {
|
|||
query.instrument.as_ref().map(|i| i.instrument_id.clone()),
|
||||
)
|
||||
.property("work-id", query.work.as_ref().map(|w| w.work_id.clone()))
|
||||
.property("prefer-recently-added", 0.0)
|
||||
.property("prefer-least-recently-played", 0.5)
|
||||
.property(
|
||||
"avoid-repeated-composers-seconds",
|
||||
"prefer-recently-added",
|
||||
settings.int("prefer-recently-added") as f64 / 100.0,
|
||||
)
|
||||
.property(
|
||||
"prefer-least-recently-played",
|
||||
settings.int("prefer-least-recently-played") as f64 / 100.0,
|
||||
)
|
||||
.property(
|
||||
"avoid-repeated-composers",
|
||||
if query.composer.is_none() && query.work.is_none() {
|
||||
3600
|
||||
settings.int("avoid-repeated-composers")
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
.property(
|
||||
"avoid-repeated-instruments-seconds",
|
||||
"avoid-repeated-instruments",
|
||||
if query.instrument.is_none() && query.work.is_none() {
|
||||
3600
|
||||
settings.int("avoid-repeated-instruments")
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
.property("play-full-recordings", true)
|
||||
.property(
|
||||
"play-full-recordings",
|
||||
settings.boolean("play-full-recordings"),
|
||||
)
|
||||
.build()
|
||||
}
|
||||
|
||||
|
|
@ -127,12 +138,12 @@ impl Program {
|
|||
data.prefer_least_recently_played.get(),
|
||||
)
|
||||
.property(
|
||||
"avoid-repeated-composers-seconds",
|
||||
data.avoid_repeated_composers_seconds.get(),
|
||||
"avoid-repeated-composers",
|
||||
data.avoid_repeated_composers.get(),
|
||||
)
|
||||
.property(
|
||||
"avoid-repeated-instruments-seconds",
|
||||
data.avoid_repeated_instruments_seconds.get(),
|
||||
"avoid-repeated-instruments",
|
||||
data.avoid_repeated_instruments.get(),
|
||||
)
|
||||
.property("play-full-recordings", data.play_full_recordings.get())
|
||||
.build();
|
||||
|
|
|
|||
89
src/slider_row.rs
Normal file
89
src/slider_row.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use std::cell::RefCell;
|
||||
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gtk::glib::{self, clone};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(glib::Properties, gtk::CompositeTemplate, Debug, Default)]
|
||||
#[properties(wrapper_type = super::SliderRow)]
|
||||
#[template(file = "data/ui/slider_row.blp")]
|
||||
pub struct SliderRow {
|
||||
#[property(get, set)]
|
||||
pub adjustment: RefCell<gtk::Adjustment>,
|
||||
|
||||
#[property(get, set)]
|
||||
pub suffix: RefCell<String>,
|
||||
|
||||
#[template_child]
|
||||
pub value_label: TemplateChild<gtk::Label>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SliderRow {
|
||||
const NAME: &'static str = "MusicusSliderRow";
|
||||
type Type = super::SliderRow;
|
||||
type ParentType = adw::PreferencesRow;
|
||||
|
||||
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 SliderRow {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
obj.connect_adjustment_notify(move |obj| {
|
||||
obj.adjustment().connect_value_changed(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |_| obj.update()
|
||||
));
|
||||
|
||||
obj.update();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for SliderRow {}
|
||||
impl ListBoxRowImpl for SliderRow {}
|
||||
impl PreferencesRowImpl for SliderRow {}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SliderRow(ObjectSubclass<imp::SliderRow>)
|
||||
@extends gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl SliderRow {
|
||||
/// Create a new slider row.
|
||||
///
|
||||
/// The adjustment can be used to control the range and initial value of the slider. Use the
|
||||
/// adjustment's `value-changed` signal for getting updates. The current value is displayed
|
||||
/// next to the slider followed by `suffix`.
|
||||
pub fn new(title: &str, adjustment: >k::Adjustment, suffix: &str) -> Self {
|
||||
glib::Object::builder()
|
||||
.property("title", title)
|
||||
.property("adjustment", adjustment)
|
||||
.property("suffix", suffix)
|
||||
.build()
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
self.imp().value_label.set_label(&format!(
|
||||
"{:.0}{}",
|
||||
self.adjustment().value(),
|
||||
self.suffix()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
use std::{cell::RefCell, path::Path};
|
||||
|
||||
use adw::subclass::prelude::*;
|
||||
use gtk::{gio, glib, glib::clone, prelude::*};
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use gettextrs::gettext;
|
||||
use gtk::{gio, glib, glib::clone};
|
||||
|
||||
use crate::{
|
||||
config,
|
||||
|
|
@ -11,15 +12,13 @@ use crate::{
|
|||
player::Player,
|
||||
player_bar::PlayerBar,
|
||||
playlist_page::PlaylistPage,
|
||||
preferences_dialog::PreferencesDialog,
|
||||
process_manager::ProcessManager,
|
||||
search_page::SearchPage,
|
||||
welcome_page::WelcomePage,
|
||||
};
|
||||
|
||||
mod imp {
|
||||
use adw::prelude::{AlertDialogExt, AlertDialogExtManual};
|
||||
use gettextrs::gettext;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, gtk::CompositeTemplate)]
|
||||
|
|
@ -89,8 +88,15 @@ mod imp {
|
|||
})
|
||||
.build();
|
||||
|
||||
let obj = self.obj().to_owned();
|
||||
let preferences_action = gio::ActionEntry::builder("preferences")
|
||||
.activate(move |_, _, _| {
|
||||
PreferencesDialog::show(&obj);
|
||||
})
|
||||
.build();
|
||||
|
||||
self.obj()
|
||||
.add_action_entries([import_action, library_action]);
|
||||
.add_action_entries([import_action, library_action, preferences_action]);
|
||||
|
||||
let player_bar = PlayerBar::new(&self.player);
|
||||
self.player_bar_revealer.set_child(Some(&player_bar));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue