Update gtk and libadwaita

This commit is contained in:
Elias Projahn 2021-10-10 10:24:18 +02:00
parent 7c9c01d3ea
commit ae2f01afda
34 changed files with 260 additions and 120 deletions

View file

@ -5,15 +5,15 @@ edition = "2018"
[dependencies]
anyhow = "1.0.33"
adw = { package = "libadwaita", version = "0.1.0-alpha" }
adw = { package = "libadwaita", version = "0.1.0-alpha-6" }
async-trait = "0.1.42"
futures-channel = "0.3.5"
gdk = { package = "gdk4", version = "0.1.0" }
gdk = { package = "gdk4", version = "0.3.0" }
gettext-rs = { version = "0.5.0", features = ["gettext-system"] }
gio = "0.14.0"
glib = "0.14.0"
gstreamer = "0.17.0"
gtk = { package = "gtk4", version = "0.1.0" }
gtk = { package = "gtk4", version = "0.3.0" }
gtk-macros = "0.3.0"
log = "0.4.14"
musicus_backend = { version = "0.1.0", path = "../backend" }

View file

@ -4,7 +4,6 @@ use crate::widgets::{ButtonRow, Editor, Section, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use log::error;
use musicus_backend::db::{Ensemble, Instrument, Performance, Person, PersonOrEnsemble};
use std::cell::RefCell;
@ -165,30 +164,30 @@ impl PerformanceEditor {
/// Update the UI according to person.
fn show_person(&self, person: Option<&Person>) {
if let Some(person) = person {
self.person_row.set_subtitle(Some(&person.name_fl()));
self.person_row.set_subtitle(&person.name_fl());
self.editor.set_may_save(true);
} else {
self.person_row.set_subtitle(None);
self.person_row.set_subtitle("");
}
}
/// Update the UI according to ensemble.
fn show_ensemble(&self, ensemble: Option<&Ensemble>) {
if let Some(ensemble) = ensemble {
self.ensemble_row.set_subtitle(Some(&ensemble.name));
self.ensemble_row.set_subtitle(&ensemble.name);
self.editor.set_may_save(true);
} else {
self.ensemble_row.set_subtitle(None);
self.ensemble_row.set_subtitle("");
}
}
/// Update the UI according to role.
fn show_role(&self, role: Option<&Instrument>) {
if let Some(role) = role {
self.role_row.set_subtitle(Some(&role.name));
self.role_row.set_subtitle(&role.name);
self.reset_role_button.show();
} else {
self.role_row.set_subtitle(None);
self.role_row.set_subtitle("");
self.reset_role_button.hide();
}
}

View file

@ -6,7 +6,6 @@ use adw::prelude::*;
use anyhow::Result;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::{generate_id, Performance, Recording, Work};
use std::cell::RefCell;
@ -139,7 +138,7 @@ impl Screen<Option<Recording>, Recording> for RecordingEditor {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&performance.get_title()));
row.set_title(&performance.get_title());
row.add_suffix(&delete_button);
row.add_suffix(&edit_button);
row.set_activatable_widget(Some(&edit_button));
@ -177,8 +176,8 @@ impl Screen<Option<Recording>, Recording> for RecordingEditor {
impl RecordingEditor {
/// Update the UI according to work.
fn work_selected(&self, work: &Work) {
self.work_row.set_title(Some(&gettext("Work")));
self.work_row.set_subtitle(Some(&work.get_title()));
self.work_row.set_title(&gettext("Work"));
self.work_row.set_subtitle(&work.get_title());
self.save_button.set_sensitive(true);
}

View file

@ -7,7 +7,6 @@ use adw::prelude::*;
use anyhow::Result;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::{generate_id, Instrument, Person, Work, WorkPart, WorkSection};
use std::cell::RefCell;
@ -163,7 +162,7 @@ impl Screen<Option<Work>, Work> for WorkEditor {
}));
let row = adw::ActionRow::new();
row.set_title(Some(&instrument.name));
row.set_title(&instrument.name);
row.add_suffix(&delete_button);
row.upcast()
@ -235,7 +234,7 @@ impl Screen<Option<Work>, Work> for WorkEditor {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&pos.get_title()));
row.set_title(&pos.get_title());
row.add_suffix(&delete_button);
row.add_suffix(&edit_button);
row.set_activatable_widget(Some(&edit_button));
@ -303,8 +302,8 @@ impl Screen<Option<Work>, Work> for WorkEditor {
impl WorkEditor {
/// Update the UI according to person.
fn show_composer(&self, person: &Person) {
self.composer_row.set_title(Some(&gettext("Composer")));
self.composer_row.set_subtitle(Some(&person.name_fl()));
self.composer_row.set_title(&gettext("Composer"));
self.composer_row.set_subtitle(&person.name_fl());
self.validate();
}

View file

@ -5,7 +5,6 @@ use crate::selectors::MediumSelector;
use crate::widgets::Widget;
use adw::prelude::*;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::Medium;
use musicus_backend::import::ImportSession;
@ -47,7 +46,7 @@ impl ImportScreen {
}
}
Err(err) => {
this.error_row.set_subtitle(Some(&err.to_string()));
this.error_row.set_subtitle(&err.to_string());
this.matching_stack.set_visible_child_name("error");
}
}

View file

@ -4,8 +4,6 @@ use crate::widgets::{List, Widget};
use adw::prelude::*;
use anyhow::Result;
use glib::clone;
use glib::prelude::*;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::{generate_id, Medium, Track};
use musicus_backend::import::ImportSession;
@ -122,8 +120,8 @@ impl Screen<(Arc<ImportSession>, Option<Medium>), Medium> for MediumEditor {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&title));
row.set_subtitle(Some(&subtitle));
row.set_title(&title);
row.set_subtitle(&subtitle);
row.add_suffix(&edit_button);
row.set_activatable_widget(Some(&edit_button));

View file

@ -2,7 +2,6 @@ use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget;
use adw::prelude::*;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::Recording;
use std::cell::RefCell;
@ -69,7 +68,7 @@ impl Screen<(Recording, Vec<usize>), Vec<usize>> for TrackEditor {
let row = adw::ActionRow::new();
row.add_prefix(&check);
row.set_activatable_widget(Some(&check));
row.set_title(Some(&part.title));
row.set_title(&part.title);
parts_list.append(&row);
}

View file

@ -2,7 +2,6 @@ use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget;
use adw::prelude::*;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::import::ImportSession;
use std::cell::RefCell;
@ -80,7 +79,7 @@ impl Screen<Arc<ImportSession>, Vec<usize>> for TrackSelector {
row.add_prefix(&check);
row.set_activatable_widget(Some(&check));
row.set_activatable(true);
row.set_title(Some(&track.name));
row.set_title(&track.name);
track_list.append(&row);
}

View file

@ -6,7 +6,6 @@ use crate::widgets::{List, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::Recording;
use musicus_backend::import::ImportSession;
@ -147,8 +146,8 @@ impl Screen<Arc<ImportSession>, TrackSetData> for TrackSetEditor {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&title));
row.set_subtitle(Some(track_name));
row.set_title(&title);
row.set_subtitle(track_name);
row.add_suffix(&edit_button);
row.set_activatable_widget(Some(&edit_button));
@ -183,10 +182,8 @@ impl TrackSetEditor {
/// Set everything up after selecting a recording.
fn recording_selected(&self) {
if let Some(recording) = &*self.recording.borrow() {
self.recording_row
.set_title(Some(&recording.work.get_title()));
self.recording_row
.set_subtitle(Some(&recording.get_performers()));
self.recording_row.set_title(&recording.work.get_title());
self.recording_row.set_subtitle(&recording.get_performers());
self.save_button.set_sensitive(true);
}

View file

@ -1,6 +1,6 @@
use super::Navigator;
use adw::prelude::*;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::Backend;
use std::rc::Rc;
@ -17,7 +17,7 @@ impl NavigatorWindow {
window.set_default_size(600, 424);
let placeholder = gtk::Label::new(None);
let navigator = Navigator::new(backend, &window, &placeholder);
adw::prelude::WindowExt::set_child(&window, Some(&navigator.widget));
window.set_content(Some(&navigator.widget));
let this = Rc::new(Self { navigator, window });

View file

@ -2,7 +2,6 @@ use crate::navigator::NavigatorWindow;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::Backend;
use std::rc::Rc;
@ -66,7 +65,7 @@ impl Preferences {
if let gtk::ResponseType::Accept = response {
if let Some(file) = dialog.file() {
if let Some(path) = file.path() {
this.music_library_path_row.set_subtitle(Some(path.to_str().unwrap()));
this.music_library_path_row.set_subtitle(path.to_str().unwrap());
spawn!(@clone this, async move {
this.backend.set_music_library_path(path).await.unwrap();
@ -85,7 +84,7 @@ impl Preferences {
let dialog = ServerDialog::new(this.backend.clone(), &this.window);
dialog.set_selected_cb(clone!(@strong this => move |url| {
this.url_row.set_subtitle(Some(&url));
this.url_row.set_subtitle(&url);
}));
dialog.show();
@ -98,9 +97,9 @@ impl Preferences {
spawn!(@clone this, async move {
if let Some(data) = replace!(window.navigator, LoginDialog, this.backend.get_login_data()).await {
if let Some(data) = data {
this.login_row.set_subtitle(Some(&data.username));
this.login_row.set_subtitle(&data.username);
} else {
this.login_row.set_subtitle(Some(&gettext("Not logged in")));
this.login_row.set_subtitle(&gettext("Not logged in"));
}
}
});
@ -110,15 +109,15 @@ impl Preferences {
if let Some(path) = this.backend.get_music_library_path() {
this.music_library_path_row
.set_subtitle(Some(path.to_str().unwrap()));
.set_subtitle(path.to_str().unwrap());
}
if let Some(url) = this.backend.get_server_url() {
this.url_row.set_subtitle(Some(&url));
this.url_row.set_subtitle(&url);
}
if let Some(data) = this.backend.get_login_data() {
this.login_row.set_subtitle(Some(&data.username));
this.login_row.set_subtitle(&data.username);
}
this

View file

@ -2,7 +2,6 @@ use crate::navigator::{NavigationHandle, Screen};
use crate::widgets::Widget;
use adw::prelude::*;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::client::{LoginData, UserRegistration};
use std::cell::RefCell;
@ -103,7 +102,7 @@ impl Screen<(), LoginData> for RegisterDialog {
spawn!(@clone this, async move {
let captcha = this.handle.backend.cl().get_captcha().await.unwrap();
this.captcha_row.set_title(Some(&captcha.question));
this.captcha_row.set_title(&captcha.question);
this.captcha_id.replace(Some(captcha.id));
this.widget.set_visible_child_name("content");
});

View file

@ -6,7 +6,6 @@ use crate::widgets::{List, Section, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Ensemble, Medium, Recording};
use std::cell::RefCell;
use std::rc::Rc;
@ -77,8 +76,8 @@ impl Screen<Ensemble, ()> for EnsembleScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&recording.work.get_title()));
row.set_subtitle(Some(&recording.get_performers()));
row.set_title(&recording.work.get_title());
row.set_subtitle(&recording.get_performers());
let recording = recording.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -106,7 +105,7 @@ impl Screen<Ensemble, ()> for EnsembleScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&medium.name));
row.set_title(&medium.name);
let medium = medium.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -7,7 +7,6 @@ use crate::widgets::{List, PlayerBar, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::PersonOrEnsemble;
use std::cell::RefCell;
@ -96,7 +95,7 @@ impl Screen<(), ()> for MainScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&poe.get_title()));
row.set_title(&poe.get_title());
let poe = poe.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -4,7 +4,6 @@ use crate::widgets::{List, Section, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::Medium;
use std::rc::Rc;
@ -80,7 +79,7 @@ impl Screen<Medium, ()> for MediumScreen {
let row = adw::ActionRow::new();
row.set_selectable(false);
row.set_activatable(false);
row.set_title(Some(&title));
row.set_title(&title);
row.set_margin_start(12);
row.upcast()

View file

@ -6,7 +6,6 @@ use crate::widgets::{List, Section, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Medium, Person, Recording, Work};
use std::cell::RefCell;
use std::rc::Rc;
@ -83,7 +82,7 @@ impl Screen<Person, ()> for PersonScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&work.title));
row.set_title(&work.title);
let work = work.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -110,8 +109,8 @@ impl Screen<Person, ()> for PersonScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&recording.work.get_title()));
row.set_subtitle(Some(&recording.get_performers()));
row.set_title(&recording.work.get_title());
row.set_subtitle(&recording.get_performers());
let recording = recording.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -139,7 +138,7 @@ impl Screen<Person, ()> for PersonScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&medium.name));
row.set_title(&medium.name);
let medium = medium.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -3,7 +3,6 @@ use crate::widgets::{List, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use gtk_macros::get_widget;
use musicus_backend::db::Track;
use std::cell::{Cell, RefCell};
@ -227,7 +226,7 @@ impl Screen<(), ()> for PlayerScreen {
let row = adw::ActionRow::new();
row.set_selectable(false);
row.set_activatable(true);
row.set_title(Some(&title));
row.set_title(&title);
if first {
let subtitle = if !parts.is_empty() {
@ -236,7 +235,7 @@ impl Screen<(), ()> for PlayerScreen {
track.recording.get_performers()
};
row.set_subtitle(Some(&subtitle));
row.set_subtitle(&subtitle);
}
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -5,7 +5,6 @@ use crate::widgets::{List, Section, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Recording, Track};
use std::cell::RefCell;
use std::rc::Rc;
@ -88,7 +87,7 @@ impl Screen<Recording, ()> for RecordingScreen {
};
let row = adw::ActionRow::new();
row.set_title(Some(&title));
row.set_title(&title);
row.upcast()
}));

View file

@ -20,7 +20,7 @@ impl Screen<(), ()> for WelcomeScreen {
.build();
let header = adw::HeaderBarBuilder::new()
.title_widget(&adw::WindowTitle::new(Some("Musicus"), None))
.title_widget(&adw::WindowTitle::new("Musicus", ""))
.build();
let button = gtk::ButtonBuilder::new()

View file

@ -6,7 +6,6 @@ use crate::widgets::{List, Section, Widget};
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Recording, Work};
use std::cell::RefCell;
use std::rc::Rc;
@ -72,8 +71,8 @@ impl Screen<Work, ()> for WorkScreen {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&recording.work.get_title()));
row.set_subtitle(Some(&recording.get_performers()));
row.set_title(&recording.work.get_title());
row.set_subtitle(&recording.get_performers());
let recording = recording.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -5,7 +5,6 @@ use crate::widgets::Widget;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::Ensemble;
use std::rc::Rc;
@ -55,7 +54,7 @@ impl Screen<(), Ensemble> for EnsembleSelector {
.set_make_widget(clone!(@weak this => @default-panic, move |ensemble| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&ensemble.name));
row.set_title(&ensemble.name);
let ensemble = ensemble.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -5,7 +5,6 @@ use crate::widgets::Widget;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::Instrument;
use std::rc::Rc;
@ -55,7 +54,7 @@ impl Screen<(), Instrument> for InstrumentSelector {
.set_make_widget(clone!(@weak this => @default-panic, move |instrument| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&instrument.name));
row.set_title(&instrument.name);
let instrument = instrument.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -4,7 +4,6 @@ use crate::widgets::Widget;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Medium, PersonOrEnsemble};
use std::rc::Rc;
@ -72,7 +71,7 @@ impl Screen<(), Medium> for MediumSelector {
this.selector.set_make_widget(clone!(@weak this => @default-panic, move |poe| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&poe.get_title()));
row.set_title(&poe.get_title());
let poe = poe.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -146,7 +145,7 @@ impl Screen<PersonOrEnsemble, Medium> for MediumSelectorMediumScreen {
.set_make_widget(clone!(@weak this => @default-panic, move |medium| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&medium.name));
row.set_title(&medium.name);
let medium = medium.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -5,7 +5,6 @@ use crate::widgets::Widget;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::Person;
use std::rc::Rc;
@ -55,7 +54,7 @@ impl Screen<(), Person> for PersonSelector {
.set_make_widget(clone!(@weak this => @default-panic, move |person| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&person.name_lf()));
row.set_title(&person.name_lf());
let person = person.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -5,7 +5,6 @@ use crate::widgets::Widget;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Person, Recording, Work};
use std::rc::Rc;
@ -64,7 +63,7 @@ impl Screen<(), Recording> for RecordingSelector {
this.selector.set_make_widget(clone!(@weak this => @default-panic, move |person| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&person.name_lf()));
row.set_title(&person.name_lf());
let person = person.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -145,7 +144,7 @@ impl Screen<Person, Work> for RecordingSelectorWorkScreen {
.set_make_widget(clone!(@weak this => @default-panic, move |work| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&work.title));
row.set_title(&work.title);
let work = work.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -212,7 +211,7 @@ impl Screen<Work, Recording> for RecordingSelectorRecordingScreen {
.set_make_widget(clone!(@weak this => @default-panic, move |recording| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&recording.get_performers()));
row.set_title(&recording.get_performers());
let recording = recording.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -137,12 +137,12 @@ impl<T> Selector<T> {
/// Set the title to be shown in the header.
pub fn set_title(&self, title: &str) {
self.title_label.set_label(&title);
self.title_label.set_label(title);
}
/// Set the subtitle to be shown in the header.
pub fn set_subtitle(&self, subtitle: &str) {
self.subtitle_label.set_label(&subtitle);
self.subtitle_label.set_label(subtitle);
self.subtitle_label.show();
}

View file

@ -5,7 +5,6 @@ use crate::widgets::Widget;
use adw::prelude::*;
use gettextrs::gettext;
use glib::clone;
use gtk::prelude::*;
use musicus_backend::db::{Person, Work};
use std::rc::Rc;
@ -58,7 +57,7 @@ impl Screen<(), Work> for WorkSelector {
this.selector.set_make_widget(clone!(@weak this => @default-panic, move |person| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&person.name_lf()));
row.set_title(&person.name_lf());
let person = person.to_owned();
row.connect_activated(clone!(@weak this => move |_| {
@ -135,7 +134,7 @@ impl Screen<Person, Work> for WorkSelectorWorkScreen {
.set_make_widget(clone!(@weak this => @default-panic, move |work| {
let row = adw::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&work.title));
row.set_title(&work.title);
let work = work.to_owned();
row.connect_activated(clone!(@weak this => move |_| {

View file

@ -1,6 +1,5 @@
use super::Widget;
use adw::prelude::*;
use gtk::prelude::*;
/// A list box row with a single button.
pub struct ButtonRow {
@ -31,7 +30,7 @@ impl ButtonRow {
}
/// Set the subtitle of the row.
pub fn set_subtitle(&self, subtitle: Option<&str>) {
pub fn set_subtitle(&self, subtitle: &str) {
self.widget.set_subtitle(subtitle);
}

View file

@ -58,7 +58,7 @@ impl Editor {
/// Show a title in the header bar.
pub fn set_title(&self, title: &str) {
self.window_title.set_title(Some(title));
self.window_title.set_title(title);
}
/// Set whether the user should be able to click the save button.
@ -78,7 +78,7 @@ impl Editor {
/// Show an error page. The page contains a button to get back to the
/// actual editor.
pub fn error(&self, title: &str, description: &str) {
self.status_page.set_title(Some(title));
self.status_page.set_title(title);
self.status_page.set_description(Some(description));
self.widget.set_visible_child_name("error");
}

View file

@ -1,5 +1,4 @@
use adw::prelude::*;
use gtk::prelude::*;
/// A list box row with an entry.
pub struct EntryRow {

View file

@ -73,12 +73,12 @@ impl Screen {
/// Show a title in the header bar.
pub fn set_title(&self, title: &str) {
self.window_title.set_title(Some(title));
self.window_title.set_title(title);
}
/// Show a subtitle in the header bar.
pub fn set_subtitle(&self, subtitle: &str) {
self.window_title.set_subtitle(Some(subtitle));
self.window_title.set_subtitle(subtitle);
}
/// Add a new item to the action menu and register a callback for it.

View file

@ -0,0 +1,143 @@
use super::{List, Widget};
use gtk::prelude::*;
use glib::clone;
use itertools::Itertools;
use musicus_backend::db::{Recording, Track, Work};
use std::{cell::RefCell, rc::Rc};
/// A widget for displaying a list of tracks.
pub struct TrackList {
tracks: RefCell<Vec<Track>>,
list: Rc<List>,
}
impl TrackList {
pub fn new() -> Rc<Self> {
let list = List::new();
let this = Rc::new(Self {
tracks: RefCell::new(Vec::new()),
list,
});
this.list.set_make_widget_cb(clone!(@weak this => move |index| {
this.track_row(index)
}));
this
}
fn track_row(&self, index: usize) -> gtk::Widget {
let tracks = self.tracks.borrow();
let track = &tracks[index];
if index > 0 {
let previous_track = &tracks[index - 1];
if previous_track.recording.id != track.recording.id {
return TrackRow::new
}
}
}
}
impl Widget for TrackList {
fn get_widget(&self) -> gtk::Widget {
self.list.get_widget()
}
}
/// Create a new separator row.
fn separator() -> gtk::ListBoxRow {
gtk::ListBoxRowBuilder::new()
.selectable(false)
.activatable(false)
.child(&gtk::Separator::new(gtk::Orientation::Horizontal))
.build()
}
/// Return an unfinished builder for a recording row.
fn recording_row(recording: &Recording) -> adw::ActionRowBuilder {
adw::ActionRowBuilder::new()
.title(&recording.work.get_title())
.subtitle(&recording.get_performers())
}
/// Get a string representing the given list of work parts.
fn parts_string(work: &Work, part_indices: &[usize]) -> String {
part_indices
.iter()
.map(|index| work.parts[*index].title.clone())
.collect::<Vec<String>>()
.join(", ")
}
/// A widget for displaying a single track within a list box.
struct TrackRow {
pub widget: gtk::ListBoxRow,
status_image: gtk::Image,
}
impl TrackRow {
/// Create a new track row.
///
/// Depending on the value of `header`, the row will display additional
/// information on the recording.
pub fn new(track: &Track, header: bool) -> Self {
let widget = gtk::ListBoxRow::new();
let content = gtk::Box::new(gtk::Orientation::Horizontal, 6);
let status_image = gtk::Image::from_icon_name(None);
content.append(&status_image);
if header {
let work_label = gtk::LabelBuilder::new()
.label(&track.recording.work.get_title())
.css_classes(vec![String::from("heading")])
.build();
let performers_label = gtk::LabelBuilder::new()
.label(&track.recording.get_performers())
.css_classes(vec![String::from("heading")])
.margin_bottom(6)
.build();
let labels = gtk::Box::new(gtk::Orientation::Vertical, 0);
labels.append(&work_label);
labels.append(&performers_label);
let title = track.title();
if !title.is_empty() {
let title_label = gtk::Label::new(Some(&track.title()));
labels.append(&title_label);
}
content.append(&labels);
} else {
content.append(&title_label);
}
widget.set_child(Some(&content));
Self {
widget,
status_image,
}
}
pub fn set_playing(&self, playing: bool) {
if playing {
self.status_image
.set_from_icon_name(Some("media-playback-start-symbolic"));
} else {
self.status_image.set_from_icon_name(None);
}
}
}
struct ListItem {
playing: bool,
header: Option<(String, String)>,
title: Option<String>,
}

View file

@ -25,7 +25,7 @@ impl Window {
.build();
let header = adw::HeaderBarBuilder::new()
.title_widget(&adw::WindowTitle::new(Some("Musicus"), None))
.title_widget(&adw::WindowTitle::new("Musicus", ""))
.build();
let spinner = gtk::SpinnerBuilder::new()
@ -42,7 +42,7 @@ impl Window {
loading_screen.append(&spinner);
let navigator = Navigator::new(Rc::clone(&backend), &window, &loading_screen);
adw::prelude::ApplicationWindowExt::set_child(&window, Some(&navigator.widget));
adw::traits::ApplicationWindowExt::set_content(&window, Some(&navigator.widget));
let this = Rc::new(Self {
backend,