summary refs log tree commit diff
path: root/src/track_selector.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/track_selector.rs')
-rw-r--r--src/track_selector.rs188
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);
+}