diff options
Diffstat (limited to 'src/track_selector.rs')
| -rw-r--r-- | src/track_selector.rs | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/src/track_selector.rs b/src/track_selector.rs new file mode 100644 index 0000000..5c56e4d --- /dev/null +++ b/src/track_selector.rs @@ -0,0 +1,188 @@ +use adw::prelude::*; +use gtk::{gio, glib}; +use relm4::prelude::*; + +use crate::tracks::StreamIndex; + +glib::wrapper! { + pub struct TrackInfo(ObjectSubclass<imp::TrackInfo>); +} + +impl TrackInfo { + pub fn new( + stream_index: StreamIndex, + language: Option<&'static str>, + title: Option<String>, + ) -> Self { + glib::Object::builder() + .property("stream-index", stream_index as i64) + .property("language", language.unwrap_or_default()) + .property("title", title.unwrap_or_default()) + .build() + } + + pub fn get_stream_index(&self) -> StreamIndex { + let index: i64 = self.property("stream-index"); + index as usize + } +} + +mod imp { + use gtk::{glib, prelude::*, subclass::prelude::*}; + use std::cell::RefCell; + + #[derive(Default, glib::Properties)] + #[properties(wrapper_type = super::TrackInfo)] + pub struct TrackInfo { + #[property(get, set)] + stream_index: RefCell<i64>, + #[property(get, set)] + language: RefCell<String>, + #[property(get, set)] + title: RefCell<String>, + } + + #[glib::object_subclass] + impl ObjectSubclass for TrackInfo { + const NAME: &'static str = "TrackInfo"; + type Type = super::TrackInfo; + } + + #[glib::derived_properties] + impl ObjectImpl for TrackInfo {} +} + +pub struct TrackSelector { + track_list_model: gio::ListStore, + track_ix: Option<StreamIndex>, +} + +pub struct TrackSelectorInit { + pub title: &'static str, + pub subtitle: Option<&'static str>, +} + +#[derive(Debug)] +pub enum TrackSelectorMsg { + SetListModel(gio::ListStore), +} + +#[derive(Debug)] +pub enum TrackSelectorOutput { + Changed(Option<StreamIndex>), +} + +#[relm4::component(pub)] +impl SimpleComponent for TrackSelector { + type Init = TrackSelectorInit; + type Input = TrackSelectorMsg; + type Output = TrackSelectorOutput; + + view! { + #[root] + #[name(primary_combo)] + adw::ComboRow { + set_title: init.title, + set_subtitle?: init.subtitle, + set_factory: Some(&track_factory), + #[watch] + set_model: Some(&model.track_list_model), + #[watch] + set_selected: model.track_ix.map_or(gtk::INVALID_LIST_POSITION, |ix| get_list_ix_from_stream_ix(&model.track_list_model, ix)), + connect_selected_notify[sender] => move |combo| { + let stream_index = get_stream_ix_from_combo(combo); + sender.output(TrackSelectorOutput::Changed(stream_index)).unwrap(); + }, + }, + + #[name(track_factory)] + gtk::SignalListItemFactory { + connect_setup => move |_, list_item| { + let list_item = list_item.downcast_ref::<gtk::ListItem>().unwrap(); + let vbox = gtk::Box::new(gtk::Orientation::Vertical, 0); + + let language_label = gtk::Label::new(None); + language_label.set_halign(gtk::Align::Start); + language_label.set_ellipsize(gtk::pango::EllipsizeMode::End); + + let title_label = gtk::Label::new(None); + title_label.set_halign(gtk::Align::Start); + title_label.set_ellipsize(gtk::pango::EllipsizeMode::End); + title_label.add_css_class("subtitle"); + + vbox.append(&language_label); + vbox.append(&title_label); + list_item.set_child(Some(&vbox)); + }, + connect_bind => move |_, list_item| { + let list_item = list_item.downcast_ref::<gtk::ListItem>().unwrap(); + let item = list_item.item().unwrap(); + let track_info = item.downcast_ref::<TrackInfo>().unwrap(); + let vbox = list_item.child().unwrap().downcast::<gtk::Box>().unwrap(); + let language_label = vbox.first_child().unwrap().downcast::<gtk::Label>().unwrap(); + let title_label = vbox.last_child().unwrap().downcast::<gtk::Label>().unwrap(); + + let language = track_info.language(); + let title = track_info.title(); + + let language_text = if !language.is_empty() { + &language + } else { + "Unknown Language" + }; + + language_label.set_text(&language_text); + title_label.set_text(&title); + title_label.set_visible(!title.is_empty()); + }, + }, + } + + fn init( + init: Self::Init, + root: Self::Root, + sender: ComponentSender<Self>, + ) -> ComponentParts<Self> { + let track_list_model = gio::ListStore::new::<TrackInfo>(); + + let model = Self { + track_list_model: track_list_model, + track_ix: None, + }; + + let widgets = view_output!(); + + ComponentParts { model, widgets } + } + + fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>) { + match msg { + TrackSelectorMsg::SetListModel(list_model) => { + self.track_list_model = list_model; + } + } + } +} + +fn get_stream_ix_from_combo(combo: &adw::ComboRow) -> Option<StreamIndex> { + let ix = combo + .selected_item()? + .downcast_ref::<TrackInfo>() + .unwrap() + .get_stream_index(); + + Some(ix) +} + +fn get_list_ix_from_stream_ix(list_model: &gio::ListStore, stream_ix: StreamIndex) -> u32 { + for i in 0..list_model.n_items() { + if let Some(item) = list_model.item(i) { + if let Some(track_info) = item.downcast_ref::<TrackInfo>() { + if track_info.get_stream_index() == stream_ix { + return i; + } + } + } + } + panic!("Stream index {} not found in list model", stream_ix); +} |