mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-27 04:07:25 +01:00
Initial port to GTK4
This commit is contained in:
parent
1a9e58d627
commit
801a130ef8
76 changed files with 3098 additions and 6625 deletions
|
|
@ -9,6 +9,7 @@ use gettextrs::gettext;
|
|||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use libhandy::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::rc::Rc;
|
||||
|
|
@ -20,6 +21,15 @@ enum PartOrSection {
|
|||
Section(WorkSection),
|
||||
}
|
||||
|
||||
impl PartOrSection {
|
||||
pub fn get_title(&self) -> String {
|
||||
match self {
|
||||
PartOrSection::Part(part) => part.title.clone(),
|
||||
PartOrSection::Section(section) => section.title.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A widget for editing and creating works.
|
||||
pub struct WorkEditor {
|
||||
widget: gtk::Stack,
|
||||
|
|
@ -27,10 +37,10 @@ pub struct WorkEditor {
|
|||
save_button: gtk::Button,
|
||||
title_entry: gtk::Entry,
|
||||
info_bar: gtk::InfoBar,
|
||||
composer_label: gtk::Label,
|
||||
composer_row: libhandy::ActionRow,
|
||||
upload_switch: gtk::Switch,
|
||||
instrument_list: Rc<List<Instrument>>,
|
||||
part_list: Rc<List<PartOrSection>>,
|
||||
instrument_list: Rc<List>,
|
||||
part_list: Rc<List>,
|
||||
id: String,
|
||||
composer: RefCell<Option<Person>>,
|
||||
instruments: RefCell<Vec<Instrument>>,
|
||||
|
|
@ -52,24 +62,19 @@ impl WorkEditor {
|
|||
get_widget!(builder, gtk::InfoBar, info_bar);
|
||||
get_widget!(builder, gtk::Entry, title_entry);
|
||||
get_widget!(builder, gtk::Button, composer_button);
|
||||
get_widget!(builder, gtk::Label, composer_label);
|
||||
get_widget!(builder, libhandy::ActionRow, composer_row);
|
||||
get_widget!(builder, gtk::Switch, upload_switch);
|
||||
get_widget!(builder, gtk::ScrolledWindow, instruments_scroll);
|
||||
get_widget!(builder, gtk::Frame, instrument_frame);
|
||||
get_widget!(builder, gtk::Button, add_instrument_button);
|
||||
get_widget!(builder, gtk::Button, remove_instrument_button);
|
||||
get_widget!(builder, gtk::ScrolledWindow, structure_scroll);
|
||||
get_widget!(builder, gtk::Frame, structure_frame);
|
||||
get_widget!(builder, gtk::Button, add_part_button);
|
||||
get_widget!(builder, gtk::Button, remove_part_button);
|
||||
get_widget!(builder, gtk::Button, add_section_button);
|
||||
get_widget!(builder, gtk::Button, edit_part_button);
|
||||
get_widget!(builder, gtk::Button, move_part_up_button);
|
||||
get_widget!(builder, gtk::Button, move_part_down_button);
|
||||
|
||||
let instrument_list = List::new(&gettext("No instruments added."));
|
||||
instruments_scroll.add(&instrument_list.widget);
|
||||
let instrument_list = List::new();
|
||||
instrument_frame.set_child(Some(&instrument_list.widget));
|
||||
|
||||
let part_list = List::new(&gettext("No work parts added."));
|
||||
structure_scroll.add(&part_list.widget);
|
||||
let part_list = List::new();
|
||||
structure_frame.set_child(Some(&part_list.widget));
|
||||
|
||||
let (id, composer, instruments, structure) = match work {
|
||||
Some(work) => {
|
||||
|
|
@ -100,7 +105,7 @@ impl WorkEditor {
|
|||
id,
|
||||
info_bar,
|
||||
title_entry,
|
||||
composer_label,
|
||||
composer_row,
|
||||
upload_switch,
|
||||
instrument_list,
|
||||
part_list,
|
||||
|
|
@ -157,16 +162,28 @@ impl WorkEditor {
|
|||
}
|
||||
}));
|
||||
|
||||
this.instrument_list.set_make_widget(|instrument| {
|
||||
let label = gtk::Label::new(Some(&instrument.name));
|
||||
label.set_ellipsize(pango::EllipsizeMode::End);
|
||||
label.set_halign(gtk::Align::Start);
|
||||
label.set_margin_start(6);
|
||||
label.set_margin_end(6);
|
||||
label.set_margin_top(6);
|
||||
label.set_margin_bottom(6);
|
||||
label.upcast()
|
||||
});
|
||||
this.instrument_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
||||
let instrument = &this.instruments.borrow()[index];
|
||||
|
||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||
delete_button.set_valign(gtk::Align::Center);
|
||||
|
||||
delete_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let length = {
|
||||
let mut instruments = this.instruments.borrow_mut();
|
||||
instruments.remove(index);
|
||||
instruments.len()
|
||||
};
|
||||
|
||||
this.instrument_list.update(length);
|
||||
}));
|
||||
|
||||
let row = libhandy::ActionRow::new();
|
||||
row.set_title(Some(&instrument.name));
|
||||
row.add_suffix(&delete_button);
|
||||
|
||||
row.upcast()
|
||||
}));
|
||||
|
||||
add_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
|
|
@ -174,17 +191,13 @@ impl WorkEditor {
|
|||
let selector = InstrumentSelector::new(this.backend.clone());
|
||||
|
||||
selector.set_selected_cb(clone!(@strong this, @strong navigator => move |instrument| {
|
||||
let mut instruments = this.instruments.borrow_mut();
|
||||
|
||||
let index = match this.instrument_list.get_selected_index() {
|
||||
Some(index) => index + 1,
|
||||
None => instruments.len(),
|
||||
let length = {
|
||||
let mut instruments = this.instruments.borrow_mut();
|
||||
instruments.push(instrument.clone());
|
||||
instruments.len()
|
||||
};
|
||||
|
||||
instruments.insert(index, instrument.clone());
|
||||
this.instrument_list.show_items(instruments.clone());
|
||||
this.instrument_list.select_index(index);
|
||||
|
||||
this.instrument_list.update(length);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
|
|
@ -192,57 +205,107 @@ impl WorkEditor {
|
|||
}
|
||||
}));
|
||||
|
||||
remove_instrument_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(index) = this.instrument_list.get_selected_index() {
|
||||
let mut instruments = this.instruments.borrow_mut();
|
||||
instruments.remove(index);
|
||||
this.instrument_list.show_items(instruments.clone());
|
||||
this.instrument_list.select_index(index);
|
||||
this.part_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
||||
let pos = &this.structure.borrow()[index];
|
||||
|
||||
let drag_source = gtk::DragSource::new();
|
||||
drag_source.set_content(Some(&gdk::ContentProvider::new_for_value(&(index as u32).to_value())));
|
||||
|
||||
let drop_target = gtk::DropTarget::new(glib::Type::U32, gdk::DragAction::MOVE);
|
||||
|
||||
drop_target.connect_property_value_notify(clone!(@strong this => move |drop_target| {
|
||||
println!("{:?} -> {:?}", drop_target.get_value(), index);
|
||||
}));
|
||||
|
||||
let handle = gtk::Image::from_icon_name(Some("open-menu-symbolic"));
|
||||
handle.add_controller(&drag_source);
|
||||
|
||||
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||
delete_button.set_valign(gtk::Align::Center);
|
||||
|
||||
delete_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.remove(index);
|
||||
structure.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
}));
|
||||
|
||||
let edit_button = gtk::Button::from_icon_name(Some("document-edit-symbolic"));
|
||||
edit_button.set_valign(gtk::Align::Center);
|
||||
|
||||
edit_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
match this.structure.borrow()[index].clone() {
|
||||
PartOrSection::Part(part) => {
|
||||
let editor = WorkPartEditor::new(this.backend.clone(), Some(part));
|
||||
|
||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure[index] = PartOrSection::Part(part);
|
||||
structure.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
navigator.push(editor);
|
||||
}
|
||||
PartOrSection::Section(section) => {
|
||||
let editor = WorkSectionEditor::new(Some(section));
|
||||
|
||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure[index] = PartOrSection::Section(section);
|
||||
structure.len()
|
||||
};
|
||||
|
||||
this.part_list.update(length);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
navigator.push(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
let row = libhandy::ActionRow::new();
|
||||
row.set_activatable(true);
|
||||
row.set_title(Some(&pos.get_title()));
|
||||
row.add_prefix(&handle);
|
||||
row.add_suffix(&delete_button);
|
||||
row.add_suffix(&edit_button);
|
||||
row.set_activatable_widget(Some(&edit_button));
|
||||
row.add_controller(&drop_target);
|
||||
|
||||
if let PartOrSection::Part(_) = pos {
|
||||
// TODO: Replace with better solution to differentiate parts and sections.
|
||||
row.set_margin_start(12);
|
||||
}
|
||||
|
||||
row.upcast()
|
||||
}));
|
||||
|
||||
this.part_list.set_make_widget(|pos| {
|
||||
let label = gtk::Label::new(None);
|
||||
label.set_ellipsize(pango::EllipsizeMode::End);
|
||||
label.set_halign(gtk::Align::Start);
|
||||
label.set_margin_end(6);
|
||||
label.set_margin_top(6);
|
||||
label.set_margin_bottom(6);
|
||||
|
||||
match pos {
|
||||
PartOrSection::Part(part) => {
|
||||
label.set_text(&part.title);
|
||||
label.set_margin_start(12);
|
||||
}
|
||||
PartOrSection::Section(section) => {
|
||||
let attrs = pango::AttrList::new();
|
||||
attrs.insert(pango::Attribute::new_weight(pango::Weight::Bold).unwrap());
|
||||
label.set_attributes(Some(&attrs));
|
||||
label.set_text(§ion.title);
|
||||
label.set_margin_start(6);
|
||||
}
|
||||
}
|
||||
|
||||
label.upcast()
|
||||
});
|
||||
|
||||
add_part_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
let editor = WorkPartEditor::new(this.backend.clone(), None);
|
||||
|
||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
|
||||
let index = match this.part_list.get_selected_index() {
|
||||
Some(index) => index + 1,
|
||||
None => structure.len(),
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.push(PartOrSection::Part(part));
|
||||
structure.len()
|
||||
};
|
||||
|
||||
structure.insert(index, PartOrSection::Part(part));
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index);
|
||||
|
||||
this.part_list.update(length);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
|
|
@ -256,17 +319,13 @@ impl WorkEditor {
|
|||
let editor = WorkSectionEditor::new(None);
|
||||
|
||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
|
||||
let index = match this.part_list.get_selected_index() {
|
||||
Some(index) => index + 1,
|
||||
None => structure.len(),
|
||||
let length = {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.push(PartOrSection::Section(section));
|
||||
structure.len()
|
||||
};
|
||||
|
||||
structure.insert(index, PartOrSection::Section(section));
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index);
|
||||
|
||||
this.part_list.update(length);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
|
|
@ -274,82 +333,14 @@ impl WorkEditor {
|
|||
}
|
||||
}));
|
||||
|
||||
edit_part_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
let navigator = this.navigator.borrow().clone();
|
||||
if let Some(navigator) = navigator {
|
||||
if let Some(index) = this.part_list.get_selected_index() {
|
||||
match this.structure.borrow()[index].clone() {
|
||||
PartOrSection::Part(part) => {
|
||||
let editor = WorkPartEditor::new(this.backend.clone(), Some(part));
|
||||
|
||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |part| {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure[index] = PartOrSection::Part(part);
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
navigator.push(editor);
|
||||
}
|
||||
PartOrSection::Section(section) => {
|
||||
let editor = WorkSectionEditor::new(Some(section));
|
||||
|
||||
editor.set_ready_cb(clone!(@strong this, @strong navigator => move |section| {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure[index] = PartOrSection::Section(section);
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index);
|
||||
navigator.clone().pop();
|
||||
}));
|
||||
|
||||
navigator.push(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
remove_part_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(index) = this.part_list.get_selected_index() {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.remove(index);
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index);
|
||||
}
|
||||
}));
|
||||
|
||||
move_part_up_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(index) = this.part_list.get_selected_index() {
|
||||
if index > 0 {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
structure.swap(index - 1, index);
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index - 1);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
move_part_down_button.connect_clicked(clone!(@strong this => move |_| {
|
||||
if let Some(index) = this.part_list.get_selected_index() {
|
||||
let mut structure = this.structure.borrow_mut();
|
||||
if index < structure.len() - 1 {
|
||||
structure.swap(index, index + 1);
|
||||
this.part_list.show_items(structure.clone());
|
||||
this.part_list.select_index(index + 1);
|
||||
}
|
||||
}
|
||||
}));
|
||||
|
||||
// Initialization
|
||||
|
||||
if let Some(composer) = &*this.composer.borrow() {
|
||||
this.show_composer(composer);
|
||||
}
|
||||
|
||||
this.instrument_list
|
||||
.show_items(this.instruments.borrow().clone());
|
||||
this.part_list.show_items(this.structure.borrow().clone());
|
||||
this.instrument_list.update(this.instruments.borrow().len());
|
||||
this.part_list.update(this.structure.borrow().len());
|
||||
|
||||
this
|
||||
}
|
||||
|
|
@ -361,7 +352,8 @@ impl WorkEditor {
|
|||
|
||||
/// Update the UI according to person.
|
||||
fn show_composer(&self, person: &Person) {
|
||||
self.composer_label.set_text(&person.name_fl());
|
||||
self.composer_row.set_title(Some(&gettext("Composer")));
|
||||
self.composer_row.set_subtitle(Some(&person.name_fl()));
|
||||
self.save_button.set_sensitive(true);
|
||||
}
|
||||
|
||||
|
|
@ -385,7 +377,7 @@ impl WorkEditor {
|
|||
|
||||
let work = Work {
|
||||
id: self.id.clone(),
|
||||
title: self.title_entry.get_text().to_string(),
|
||||
title: self.title_entry.get_text().unwrap().to_string(),
|
||||
composer: self
|
||||
.composer
|
||||
.borrow()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue