mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-27 04:07:25 +01:00
Move desktop app to subdirectory
This commit is contained in:
parent
ea3bd35ffd
commit
775f3ffe90
109 changed files with 64 additions and 53 deletions
|
|
@ -1,129 +0,0 @@
|
|||
use super::*;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
use std::convert::TryInto;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct List<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
pub widget: gtk::ListBox,
|
||||
items: RefCell<Vec<T>>,
|
||||
make_widget: RefCell<Option<Box<dyn Fn(&T) -> gtk::Widget>>>,
|
||||
filter: RefCell<Option<Box<dyn Fn(&T) -> bool>>>,
|
||||
selected: RefCell<Option<Box<dyn Fn(&T) -> ()>>>,
|
||||
}
|
||||
|
||||
impl<T> List<T>
|
||||
where
|
||||
T: 'static,
|
||||
{
|
||||
pub fn new(placeholder_text: &str) -> Rc<Self> {
|
||||
let placeholder_label = gtk::Label::new(Some(placeholder_text));
|
||||
placeholder_label.set_margin_top(6);
|
||||
placeholder_label.set_margin_bottom(6);
|
||||
placeholder_label.set_margin_start(6);
|
||||
placeholder_label.set_margin_end(6);
|
||||
placeholder_label.show();
|
||||
|
||||
let widget = gtk::ListBox::new();
|
||||
widget.set_placeholder(Some(&placeholder_label));
|
||||
widget.show();
|
||||
|
||||
let this = Rc::new(Self {
|
||||
widget,
|
||||
items: RefCell::new(Vec::new()),
|
||||
make_widget: RefCell::new(None),
|
||||
filter: RefCell::new(None),
|
||||
selected: RefCell::new(None),
|
||||
});
|
||||
|
||||
this.widget
|
||||
.connect_row_activated(clone!(@strong this => move |_, row| {
|
||||
if let Some(selected) = &*this.selected.borrow() {
|
||||
let row = row.get_child().unwrap().downcast::<SelectorRow>().unwrap();
|
||||
let index: usize = row.get_index().try_into().unwrap();
|
||||
selected(&this.items.borrow()[index]);
|
||||
}
|
||||
}));
|
||||
|
||||
this.widget
|
||||
.set_filter_func(Some(Box::new(clone!(@strong this => move |row| {
|
||||
if let Some(filter) = &*this.filter.borrow() {
|
||||
let row = row.get_child().unwrap().downcast::<SelectorRow>().unwrap();
|
||||
let index: usize = row.get_index().try_into().unwrap();
|
||||
filter(&this.items.borrow()[index])
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}))));
|
||||
|
||||
this
|
||||
}
|
||||
|
||||
pub fn set_make_widget<F: Fn(&T) -> gtk::Widget + 'static>(&self, make_widget: F) {
|
||||
self.make_widget.replace(Some(Box::new(make_widget)));
|
||||
}
|
||||
|
||||
pub fn set_filter<F: Fn(&T) -> bool + 'static>(&self, filter: F) {
|
||||
self.filter.replace(Some(Box::new(filter)));
|
||||
}
|
||||
|
||||
pub fn set_selected<S: Fn(&T) -> () + 'static>(&self, selected: S) {
|
||||
self.selected.replace(Some(Box::new(selected)));
|
||||
}
|
||||
|
||||
pub fn get_selected_index(&self) -> Option<usize> {
|
||||
match self.widget.get_selected_rows().first() {
|
||||
Some(row) => match row.get_child() {
|
||||
Some(child) => Some(
|
||||
child
|
||||
.downcast::<SelectorRow>()
|
||||
.unwrap()
|
||||
.get_index()
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
),
|
||||
None => None,
|
||||
},
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_index(&self, index: usize) {
|
||||
self.widget.select_row(
|
||||
self.widget
|
||||
.get_row_at_index(index.try_into().unwrap())
|
||||
.as_ref(),
|
||||
);
|
||||
}
|
||||
|
||||
pub fn show_items(&self, items: Vec<T>) {
|
||||
self.items.replace(items);
|
||||
self.update();
|
||||
}
|
||||
|
||||
pub fn invalidate_filter(&self) {
|
||||
self.widget.invalidate_filter();
|
||||
}
|
||||
|
||||
pub fn update(&self) {
|
||||
for child in self.widget.get_children() {
|
||||
self.widget.remove(&child);
|
||||
}
|
||||
|
||||
if let Some(make_widget) = &*self.make_widget.borrow() {
|
||||
for (index, item) in self.items.borrow().iter().enumerate() {
|
||||
let row = SelectorRow::new(index.try_into().unwrap(), &make_widget(item));
|
||||
row.show_all();
|
||||
self.widget.insert(&row, -1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_selection(&self) {
|
||||
self.widget.unselect_all();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
pub mod list;
|
||||
pub use list::*;
|
||||
|
||||
pub mod navigator;
|
||||
pub use navigator::*;
|
||||
|
||||
pub mod person_list;
|
||||
pub use person_list::*;
|
||||
|
||||
pub mod player_bar;
|
||||
pub use player_bar::*;
|
||||
|
||||
pub mod poe_list;
|
||||
pub use poe_list::*;
|
||||
|
||||
pub mod selector_row;
|
||||
pub use selector_row::*;
|
||||
|
|
@ -1,139 +0,0 @@
|
|||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub trait NavigatorScreen {
|
||||
fn attach_navigator(&self, navigator: Rc<Navigator>);
|
||||
fn get_widget(&self) -> gtk::Widget;
|
||||
fn detach_navigator(&self);
|
||||
}
|
||||
|
||||
pub struct Navigator {
|
||||
pub widget: gtk::Stack,
|
||||
screens: RefCell<Vec<Rc<dyn NavigatorScreen>>>,
|
||||
old_screens: RefCell<Vec<Rc<dyn NavigatorScreen>>>,
|
||||
back_cb: RefCell<Option<Box<dyn Fn() -> ()>>>,
|
||||
}
|
||||
|
||||
impl Navigator {
|
||||
pub fn new<W>(empty_screen: &W) -> Rc<Self>
|
||||
where
|
||||
W: IsA<gtk::Widget>,
|
||||
{
|
||||
let widget = gtk::Stack::new();
|
||||
widget.set_transition_type(gtk::StackTransitionType::Crossfade);
|
||||
widget.set_hexpand(true);
|
||||
widget.add_named(empty_screen, "empty_screen");
|
||||
widget.show();
|
||||
|
||||
let result = Rc::new(Self {
|
||||
widget,
|
||||
screens: RefCell::new(Vec::new()),
|
||||
old_screens: RefCell::new(Vec::new()),
|
||||
back_cb: RefCell::new(None),
|
||||
});
|
||||
|
||||
unsafe {
|
||||
result.widget.connect_notify_unsafe(
|
||||
Some("transition-running"),
|
||||
clone!(@strong result => move |_, _| {
|
||||
if !result.widget.get_transition_running() {
|
||||
result.clear_old_screens();
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn set_back_cb<F>(&self, cb: F) where F: Fn() -> () + 'static {
|
||||
self.back_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
|
||||
pub fn push<S>(self: Rc<Self>, screen: Rc<S>)
|
||||
where
|
||||
S: NavigatorScreen + 'static,
|
||||
{
|
||||
if let Some(screen) = self.screens.borrow().last() {
|
||||
screen.detach_navigator();
|
||||
}
|
||||
|
||||
let widget = screen.get_widget();
|
||||
self.widget.add(&widget);
|
||||
self.widget.set_visible_child(&widget);
|
||||
|
||||
screen.attach_navigator(self.clone());
|
||||
self.screens.borrow_mut().push(screen);
|
||||
}
|
||||
|
||||
pub fn pop(self: Rc<Self>) {
|
||||
let popped = if let Some(screen) = self.screens.borrow_mut().pop() {
|
||||
screen.detach_navigator();
|
||||
self.old_screens.borrow_mut().push(screen);
|
||||
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if popped {
|
||||
if let Some(screen) = self.screens.borrow().last() {
|
||||
let widget = screen.get_widget();
|
||||
self.widget.set_visible_child(&widget);
|
||||
|
||||
screen.attach_navigator(self.clone());
|
||||
} else {
|
||||
self.widget.set_visible_child_name("empty_screen");
|
||||
if let Some(cb) = &*self.back_cb.borrow() {
|
||||
cb()
|
||||
}
|
||||
}
|
||||
|
||||
if !self.widget.get_transition_running() {
|
||||
self.clear_old_screens();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn replace<S>(self: Rc<Self>, screen: Rc<S>)
|
||||
where
|
||||
S: NavigatorScreen + 'static,
|
||||
{
|
||||
for screen in self.screens.replace(Vec::new()) {
|
||||
screen.detach_navigator();
|
||||
self.old_screens.borrow_mut().push(screen);
|
||||
}
|
||||
|
||||
let widget = screen.get_widget();
|
||||
self.widget.add(&widget);
|
||||
self.widget.set_visible_child(&widget);
|
||||
|
||||
screen.attach_navigator(self.clone());
|
||||
self.screens.borrow_mut().push(screen);
|
||||
|
||||
if !self.widget.get_transition_running() {
|
||||
self.clear_old_screens();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn reset(&self) {
|
||||
for screen in self.screens.replace(Vec::new()) {
|
||||
screen.detach_navigator();
|
||||
self.old_screens.borrow_mut().push(screen);
|
||||
}
|
||||
|
||||
if !self.widget.get_transition_running() {
|
||||
self.clear_old_screens();
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_old_screens(&self) {
|
||||
for screen in self.old_screens.borrow().iter() {
|
||||
self.widget.remove(&screen.get_widget());
|
||||
}
|
||||
|
||||
self.old_screens.borrow_mut().clear();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,82 +0,0 @@
|
|||
use super::*;
|
||||
use crate::backend::Backend;
|
||||
use crate::database::*;
|
||||
use gettextrs::gettext;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct PersonList {
|
||||
pub widget: gtk::Box,
|
||||
list: Rc<List<Person>>,
|
||||
backend: Rc<Backend>,
|
||||
stack: gtk::Stack,
|
||||
}
|
||||
|
||||
impl PersonList {
|
||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/person_list.ui");
|
||||
|
||||
get_widget!(builder, gtk::Box, widget);
|
||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
||||
get_widget!(builder, gtk::Stack, stack);
|
||||
get_widget!(builder, gtk::ScrolledWindow, scrolled_window);
|
||||
|
||||
let list = List::new(&gettext("No persons found."));
|
||||
|
||||
list.set_make_widget(|person: &Person| {
|
||||
let label = gtk::Label::new(Some(&person.name_lf()));
|
||||
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()
|
||||
});
|
||||
|
||||
list.set_filter(clone!(@strong search_entry => move |person: &Person| {
|
||||
let search = search_entry.get_text().to_string().to_lowercase();
|
||||
let name = person.name_fl().to_lowercase();
|
||||
search.is_empty() || name.contains(&search)
|
||||
}));
|
||||
|
||||
scrolled_window.add(&list.widget);
|
||||
|
||||
let result = Rc::new(Self {
|
||||
widget,
|
||||
list,
|
||||
backend,
|
||||
stack,
|
||||
});
|
||||
|
||||
search_entry.connect_search_changed(clone!(@strong result => move |_| {
|
||||
result.list.invalidate_filter();
|
||||
}));
|
||||
|
||||
result.clone().reload();
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn set_selected<S>(&self, selected: S)
|
||||
where
|
||||
S: Fn(&Person) -> () + 'static,
|
||||
{
|
||||
self.list.set_selected(selected);
|
||||
}
|
||||
|
||||
pub fn reload(self: Rc<Self>) {
|
||||
self.stack.set_visible_child_name("loading");
|
||||
|
||||
let context = glib::MainContext::default();
|
||||
let backend = self.backend.clone();
|
||||
let list = self.list.clone();
|
||||
|
||||
context.spawn_local(async move {
|
||||
let persons = backend.get_persons().await.unwrap();
|
||||
list.show_items(persons);
|
||||
self.stack.set_visible_child_name("content");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,175 +0,0 @@
|
|||
use crate::player::*;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
pub struct PlayerBar {
|
||||
pub widget: gtk::Revealer,
|
||||
title_label: gtk::Label,
|
||||
subtitle_label: gtk::Label,
|
||||
previous_button: gtk::Button,
|
||||
play_button: gtk::Button,
|
||||
next_button: gtk::Button,
|
||||
position_label: gtk::Label,
|
||||
duration_label: gtk::Label,
|
||||
play_image: gtk::Image,
|
||||
pause_image: gtk::Image,
|
||||
player: Rc<RefCell<Option<Rc<Player>>>>,
|
||||
playlist_cb: Rc<RefCell<Option<Box<dyn Fn() -> ()>>>>,
|
||||
}
|
||||
|
||||
impl PlayerBar {
|
||||
pub fn new() -> Self {
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/player_bar.ui");
|
||||
|
||||
get_widget!(builder, gtk::Revealer, widget);
|
||||
get_widget!(builder, gtk::Label, title_label);
|
||||
get_widget!(builder, gtk::Label, subtitle_label);
|
||||
get_widget!(builder, gtk::Button, previous_button);
|
||||
get_widget!(builder, gtk::Button, play_button);
|
||||
get_widget!(builder, gtk::Button, next_button);
|
||||
get_widget!(builder, gtk::Label, position_label);
|
||||
get_widget!(builder, gtk::Label, duration_label);
|
||||
get_widget!(builder, gtk::Button, playlist_button);
|
||||
get_widget!(builder, gtk::Image, play_image);
|
||||
get_widget!(builder, gtk::Image, pause_image);
|
||||
|
||||
let player = Rc::new(RefCell::new(None::<Rc<Player>>));
|
||||
let playlist_cb = Rc::new(RefCell::new(None::<Box<dyn Fn() -> ()>>));
|
||||
|
||||
previous_button.connect_clicked(clone!(@strong player => move |_| {
|
||||
if let Some(player) = &*player.borrow() {
|
||||
player.previous().unwrap();
|
||||
}
|
||||
}));
|
||||
|
||||
play_button.connect_clicked(clone!(@strong player => move |_| {
|
||||
if let Some(player) = &*player.borrow() {
|
||||
player.play_pause();
|
||||
}
|
||||
}));
|
||||
|
||||
next_button.connect_clicked(clone!(@strong player => move |_| {
|
||||
if let Some(player) = &*player.borrow() {
|
||||
player.next().unwrap();
|
||||
}
|
||||
}));
|
||||
|
||||
playlist_button.connect_clicked(clone!(@strong playlist_cb => move |_| {
|
||||
if let Some(cb) = &*playlist_cb.borrow() {
|
||||
cb();
|
||||
}
|
||||
}));
|
||||
|
||||
Self {
|
||||
widget,
|
||||
title_label,
|
||||
subtitle_label,
|
||||
previous_button,
|
||||
play_button,
|
||||
next_button,
|
||||
position_label,
|
||||
duration_label,
|
||||
play_image,
|
||||
pause_image,
|
||||
player: player,
|
||||
playlist_cb: playlist_cb,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_player(&self, player: Option<Rc<Player>>) {
|
||||
self.player.replace(player.clone());
|
||||
|
||||
if let Some(player) = player {
|
||||
let playlist = Rc::new(RefCell::new(Vec::<PlaylistItem>::new()));
|
||||
|
||||
player.add_playlist_cb(clone!(
|
||||
@strong player,
|
||||
@strong self.widget as widget,
|
||||
@strong self.previous_button as previous_button,
|
||||
@strong self.next_button as next_button,
|
||||
@strong playlist
|
||||
=> move |new_playlist| {
|
||||
widget.set_reveal_child(!new_playlist.is_empty());
|
||||
playlist.replace(new_playlist);
|
||||
previous_button.set_sensitive(player.has_previous());
|
||||
next_button.set_sensitive(player.has_next());
|
||||
}
|
||||
));
|
||||
|
||||
player.add_track_cb(clone!(
|
||||
@strong player,
|
||||
@strong playlist,
|
||||
@strong self.previous_button as previous_button,
|
||||
@strong self.next_button as next_button,
|
||||
@strong self.title_label as title_label,
|
||||
@strong self.subtitle_label as subtitle_label,
|
||||
@strong self.position_label as position_label
|
||||
=> move |current_item, current_track| {
|
||||
previous_button.set_sensitive(player.has_previous());
|
||||
next_button.set_sensitive(player.has_next());
|
||||
|
||||
let item = &playlist.borrow()[current_item];
|
||||
let track = &item.tracks[current_track];
|
||||
|
||||
let mut parts = Vec::<String>::new();
|
||||
for part in &track.work_parts {
|
||||
parts.push(item.recording.work.parts[*part].title.clone());
|
||||
}
|
||||
|
||||
let mut title = item.recording.work.get_title();
|
||||
if !parts.is_empty() {
|
||||
title = format!("{}: {}", title, parts.join(", "));
|
||||
}
|
||||
|
||||
title_label.set_text(&title);
|
||||
subtitle_label.set_text(&item.recording.get_performers());
|
||||
position_label.set_text("0:00");
|
||||
}
|
||||
));
|
||||
|
||||
player.add_duration_cb(clone!(
|
||||
@strong self.duration_label as duration_label
|
||||
=> move |ms| {
|
||||
let min = ms / 60000;
|
||||
let sec = (ms % 60000) / 1000;
|
||||
duration_label.set_text(&format!("{}:{:02}", min, sec));
|
||||
}
|
||||
));
|
||||
|
||||
player.add_playing_cb(clone!(
|
||||
@strong self.play_button as play_button,
|
||||
@strong self.play_image as play_image,
|
||||
@strong self.pause_image as pause_image
|
||||
=> move |playing| {
|
||||
if let Some(child) = play_button.get_child() {
|
||||
play_button.remove( &child);
|
||||
}
|
||||
|
||||
play_button.add(if playing {
|
||||
&pause_image
|
||||
} else {
|
||||
&play_image
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
player.add_position_cb(clone!(
|
||||
@strong self.position_label as position_label
|
||||
=> move |ms| {
|
||||
let min = ms / 60000;
|
||||
let sec = (ms % 60000) / 1000;
|
||||
position_label.set_text(&format!("{}:{:02}", min, sec));
|
||||
}
|
||||
));
|
||||
} else {
|
||||
self.widget.set_reveal_child(false);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_playlist_cb<F: Fn() -> () + 'static>(&self, cb: F) {
|
||||
self.playlist_cb.replace(Some(Box::new(cb)));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
use super::*;
|
||||
use crate::backend::Backend;
|
||||
use crate::database::*;
|
||||
use gettextrs::gettext;
|
||||
use glib::clone;
|
||||
use gtk::prelude::*;
|
||||
use gtk_macros::get_widget;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum PersonOrEnsemble {
|
||||
Person(Person),
|
||||
Ensemble(Ensemble),
|
||||
}
|
||||
|
||||
impl PersonOrEnsemble {
|
||||
pub fn get_title(&self) -> String {
|
||||
match self {
|
||||
PersonOrEnsemble::Person(person) => person.name_lf(),
|
||||
PersonOrEnsemble::Ensemble(ensemble) => ensemble.name.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PoeList {
|
||||
pub widget: gtk::Box,
|
||||
list: Rc<List<PersonOrEnsemble>>,
|
||||
backend: Rc<Backend>,
|
||||
stack: gtk::Stack,
|
||||
}
|
||||
|
||||
impl PoeList {
|
||||
pub fn new(backend: Rc<Backend>) -> Rc<Self> {
|
||||
let builder = gtk::Builder::from_resource("/de/johrpan/musicus/ui/poe_list.ui");
|
||||
|
||||
get_widget!(builder, gtk::Box, widget);
|
||||
get_widget!(builder, gtk::SearchEntry, search_entry);
|
||||
get_widget!(builder, gtk::Stack, stack);
|
||||
get_widget!(builder, gtk::ScrolledWindow, scrolled_window);
|
||||
|
||||
let list = List::new(&gettext("No persons or ensembles found."));
|
||||
|
||||
list.set_make_widget(|poe: &PersonOrEnsemble| {
|
||||
let label = gtk::Label::new(Some(&poe.get_title()));
|
||||
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()
|
||||
});
|
||||
|
||||
list.set_filter(
|
||||
clone!(@strong search_entry => move |poe: &PersonOrEnsemble| {
|
||||
let search = search_entry.get_text().to_string().to_lowercase();
|
||||
let title = poe.get_title().to_lowercase();
|
||||
search.is_empty() || title.contains(&search)
|
||||
}),
|
||||
);
|
||||
|
||||
scrolled_window.add(&list.widget);
|
||||
|
||||
let result = Rc::new(Self {
|
||||
widget,
|
||||
list,
|
||||
backend,
|
||||
stack,
|
||||
});
|
||||
|
||||
search_entry.connect_search_changed(clone!(@strong result => move |_| {
|
||||
result.list.invalidate_filter();
|
||||
}));
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
pub fn set_selected<S>(&self, selected: S)
|
||||
where
|
||||
S: Fn(&PersonOrEnsemble) -> () + 'static,
|
||||
{
|
||||
self.list.set_selected(selected);
|
||||
}
|
||||
|
||||
pub fn reload(self: Rc<Self>) {
|
||||
self.stack.set_visible_child_name("loading");
|
||||
|
||||
let context = glib::MainContext::default();
|
||||
let backend = self.backend.clone();
|
||||
let list = self.list.clone();
|
||||
|
||||
context.spawn_local(async move {
|
||||
let persons = backend.get_persons().await.unwrap();
|
||||
let ensembles = backend.get_ensembles().await.unwrap();
|
||||
let mut poes: Vec<PersonOrEnsemble> = Vec::new();
|
||||
|
||||
for person in persons {
|
||||
poes.push(PersonOrEnsemble::Person(person));
|
||||
}
|
||||
|
||||
for ensemble in ensembles {
|
||||
poes.push(PersonOrEnsemble::Ensemble(ensemble));
|
||||
}
|
||||
|
||||
list.show_items(poes);
|
||||
|
||||
self.stack.set_visible_child_name("content");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use glib::translate::*;
|
||||
use glib::{glib_object_impl, glib_object_subclass, glib_wrapper};
|
||||
use gtk::prelude::*;
|
||||
use gtk::subclass::prelude::*;
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
glib_wrapper! {
|
||||
pub struct SelectorRow(
|
||||
Object<subclass::simple::InstanceStruct<SelectorRowPriv>,
|
||||
subclass::simple::ClassStruct<SelectorRowPriv>,
|
||||
SelectorRowClass>
|
||||
) @extends gtk::Bin, gtk::Container, gtk::Widget;
|
||||
|
||||
match fn {
|
||||
get_type => || SelectorRowPriv::get_type().to_glib(),
|
||||
}
|
||||
}
|
||||
|
||||
impl SelectorRow {
|
||||
pub fn new<T: IsA<gtk::Widget>>(index: u64, child: &T) -> Self {
|
||||
glib::Object::new(
|
||||
Self::static_type(),
|
||||
&[("index", &index), ("child", child.upcast_ref())],
|
||||
)
|
||||
.expect("Failed to create SelectorRow GObject!")
|
||||
.downcast()
|
||||
.expect("SelectorRow GObject is of the wrong type!")
|
||||
}
|
||||
|
||||
pub fn get_index(&self) -> u64 {
|
||||
self.get_property("index").unwrap().get().unwrap().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct SelectorRowPriv {
|
||||
index: Cell<u64>,
|
||||
child: RefCell<Option<gtk::Widget>>,
|
||||
}
|
||||
|
||||
static PROPERTIES: [subclass::Property; 2] = [
|
||||
subclass::Property("index", |name| {
|
||||
glib::ParamSpec::uint64(
|
||||
name,
|
||||
"Index",
|
||||
"Index",
|
||||
0,
|
||||
u64::MAX,
|
||||
0,
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
subclass::Property("child", |name| {
|
||||
glib::ParamSpec::object(
|
||||
name,
|
||||
"Child",
|
||||
"Child",
|
||||
gtk::Widget::static_type(),
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
];
|
||||
|
||||
impl ObjectSubclass for SelectorRowPriv {
|
||||
const NAME: &'static str = "SelectorRow";
|
||||
type ParentType = gtk::Bin;
|
||||
type Instance = subclass::simple::InstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
glib_object_subclass!();
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
klass.install_properties(&PROPERTIES);
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
index: Cell::new(0),
|
||||
child: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for SelectorRowPriv {
|
||||
glib_object_impl!();
|
||||
|
||||
fn constructed(&self, object: &glib::Object) {
|
||||
self.parent_constructed(object);
|
||||
|
||||
let row = object.downcast_ref::<SelectorRow>().unwrap();
|
||||
|
||||
let child = self.child.borrow();
|
||||
match child.as_ref() {
|
||||
Some(child) => row.add(child),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property(&self, object: &glib::Object, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
subclass::Property("index", ..) => {
|
||||
let index = value
|
||||
.get_some()
|
||||
.expect("Wrong type for SelectorRow GObject index property!");
|
||||
self.index.set(index);
|
||||
}
|
||||
subclass::Property("child", ..) => {
|
||||
let child = value
|
||||
.get()
|
||||
.expect("Wrong type for SelectorRow GObject child property!");
|
||||
|
||||
let row = object.downcast_ref::<SelectorRow>().unwrap();
|
||||
|
||||
{
|
||||
let old = self.child.borrow();
|
||||
match old.as_ref() {
|
||||
Some(old) => row.remove(old),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
self.child.replace(child.clone());
|
||||
match child {
|
||||
Some(child) => row.add(&child),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
subclass::Property("index", ..) => Ok(self.index.get().to_value()),
|
||||
subclass::Property("child", ..) => Ok(self.child.borrow().to_value()),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for SelectorRowPriv {}
|
||||
impl ContainerImpl for SelectorRowPriv {}
|
||||
impl BinImpl for SelectorRowPriv {}
|
||||
Loading…
Add table
Add a link
Reference in a new issue