summary refs log tree commit diff
path: root/src/subtitle_selection_dialog.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/subtitle_selection_dialog.rs')
-rw-r--r--src/subtitle_selection_dialog.rs214
1 files changed, 55 insertions, 159 deletions
diff --git a/src/subtitle_selection_dialog.rs b/src/subtitle_selection_dialog.rs
index 0c7f1cd..6136d56 100644
--- a/src/subtitle_selection_dialog.rs
+++ b/src/subtitle_selection_dialog.rs
@@ -1,63 +1,18 @@
 use adw::prelude::*;
-use gtk::{gio, glib};
+use gtk::gio;
 use relm4::prelude::*;
 
-use crate::subtitle_extractor::{StreamIndex, TRACKS};
-use crate::util::Tracker;
-
-// Custom GObject wrapper for subtitle track information
-glib::wrapper! {
-    pub struct SubtitleTrackInfo(ObjectSubclass<imp::SubtitleTrackInfo>);
-}
-
-impl SubtitleTrackInfo {
-    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::SubtitleTrackInfo)]
-    pub struct SubtitleTrackInfo {
-        #[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 SubtitleTrackInfo {
-        const NAME: &'static str = "SubtitleTrackInfo";
-        type Type = super::SubtitleTrackInfo;
-    }
-
-    #[glib::derived_properties]
-    impl ObjectImpl for SubtitleTrackInfo {}
-}
+use crate::track_selector::{
+    TrackInfo, TrackSelector, TrackSelectorInit, TrackSelectorMsg, TrackSelectorOutput,
+};
+use crate::tracks::{SUBTITLE_TRACKS, StreamIndex};
 
 pub struct SubtitleSelectionDialog {
     parent_window: adw::ApplicationWindow,
     dialog: adw::PreferencesDialog,
-    track_list_model: Tracker<gio::ListStore>,
+    track_list_model: gio::ListStore,
+    primary_selector: Controller<TrackSelector>,
+    secondary_selector: Controller<TrackSelector>,
     primary_track_ix: Option<StreamIndex>,
     secondary_track_ix: Option<StreamIndex>,
 }
@@ -91,79 +46,10 @@ impl SimpleComponent for SubtitleSelectionDialog {
         #[name(page)]
         adw::PreferencesPage {
             adw::PreferencesGroup {
-                #[name(primary_combo)]
-                adw::ComboRow {
-                    set_title: "Primary Subtitle Track",
-                    set_subtitle: "Main subtitle track for learning",
-                    set_factory: Some(&track_factory),
-                    #[track(model.track_list_model.is_dirty())]
-                    set_model: Some(model.track_list_model.get()),
-                    #[track(model.track_list_model.is_dirty())]
-                    set_selected: model.primary_track_ix.map_or(gtk::INVALID_LIST_POSITION, |ix| get_list_ix_from_stream_ix(model.track_list_model.get(), ix)),
-                    connect_selected_notify[sender] => move |combo| {
-                        let stream_index = get_stream_ix_from_combo(combo);
-                        sender.input(SubtitleSelectionDialogMsg::PrimaryTrackChanged(stream_index));
-                    },
-                },
-
-                #[name(secondary_combo)]
-                adw::ComboRow {
-                    set_title: "Secondary Subtitle Track",
-                    set_subtitle: "Optional second track for comparison",
-                    set_factory: Some(&track_factory),
-                    #[track(model.track_list_model.is_dirty())]
-                    set_model: Some(model.track_list_model.get()),
-                    #[track(model.track_list_model.is_dirty())]
-                    set_selected: model.secondary_track_ix.map_or(gtk::INVALID_LIST_POSITION, |ix| get_list_ix_from_stream_ix(model.track_list_model.get(), ix)),
-                    connect_selected_notify[sender] => move |combo| {
-                        let stream_index = get_stream_ix_from_combo(combo);
-                        sender.input(SubtitleSelectionDialogMsg::SecondaryTrackChanged(stream_index));
-                    },
-                },
+                model.primary_selector.widget(),
+                model.secondary_selector.widget(),
             }
         },
-
-        #[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::<SubtitleTrackInfo>().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(
@@ -171,12 +57,33 @@ impl SimpleComponent for SubtitleSelectionDialog {
         root: Self::Root,
         sender: ComponentSender<Self>,
     ) -> ComponentParts<Self> {
-        let track_list_model = gio::ListStore::new::<SubtitleTrackInfo>();
+        let primary_selector = TrackSelector::builder()
+            .launch(TrackSelectorInit {
+                title: "Primary subtitle track",
+                subtitle: Some("Select your target language here"),
+            })
+            .forward(sender.input_sender(), |output| match output {
+                TrackSelectorOutput::Changed(ix) => {
+                    SubtitleSelectionDialogMsg::PrimaryTrackChanged(ix)
+                }
+            });
+        let secondary_selector = TrackSelector::builder()
+            .launch(TrackSelectorInit {
+                title: "Secondary subtitle track",
+                subtitle: Some("Pick a language you already know"),
+            })
+            .forward(sender.input_sender(), |output| match output {
+                TrackSelectorOutput::Changed(ix) => {
+                    SubtitleSelectionDialogMsg::SecondaryTrackChanged(ix)
+                }
+            });
 
         let model = Self {
             parent_window,
             dialog: root.clone(),
-            track_list_model: Tracker::new(track_list_model),
+            track_list_model: gio::ListStore::new::<TrackInfo>(),
+            primary_selector,
+            secondary_selector,
             primary_track_ix: None,
             secondary_track_ix: None,
         };
@@ -187,11 +94,23 @@ impl SimpleComponent for SubtitleSelectionDialog {
     }
 
     fn update(&mut self, msg: Self::Input, sender: ComponentSender<Self>) {
-        self.track_list_model.reset();
-
         match msg {
             SubtitleSelectionDialogMsg::Show => {
-                self.update_combo_models();
+                self.update_track_list_model();
+
+                self.primary_selector
+                    .sender()
+                    .send(TrackSelectorMsg::SetListModel(
+                        self.track_list_model.clone(),
+                    ))
+                    .unwrap();
+                self.secondary_selector
+                    .sender()
+                    .send(TrackSelectorMsg::SetListModel(
+                        self.track_list_model.clone(),
+                    ))
+                    .unwrap();
+
                 self.dialog.present(Some(&self.parent_window));
             }
             SubtitleSelectionDialogMsg::PrimaryTrackChanged(stream_index) => {
@@ -215,43 +134,20 @@ impl SimpleComponent for SubtitleSelectionDialog {
 }
 
 impl SubtitleSelectionDialog {
-    fn update_combo_models(&mut self) {
-        let tracks = TRACKS.read();
+    fn update_track_list_model(&mut self) {
+        let tracks = SUBTITLE_TRACKS.read();
 
         // Clear previous entries
-        self.track_list_model.get_mut().remove_all();
+        self.track_list_model.remove_all();
 
         // Add all available tracks
         for (&stream_index, track) in tracks.iter() {
-            let track_info = SubtitleTrackInfo::new(
+            let track_info = TrackInfo::new(
                 stream_index,
-                track.language.map(|lang| lang.to_name()),
-                track.title.clone(),
+                track.metadata.language.map(|lang| lang.to_name()),
+                track.metadata.title.clone(),
             );
-            self.track_list_model.get_mut().append(&track_info);
-        }
-    }
-}
-
-fn get_stream_ix_from_combo(combo: &adw::ComboRow) -> Option<StreamIndex> {
-    let ix = combo
-        .selected_item()?
-        .downcast_ref::<SubtitleTrackInfo>()
-        .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::<SubtitleTrackInfo>() {
-                if track_info.get_stream_index() == stream_ix {
-                    return i;
-                }
-            }
+            self.track_list_model.append(&track_info);
         }
     }
-    panic!("Stream index {} not found in list model", stream_ix);
 }