use crate::cue_view::{CueView, CueViewMsg, CueViewOutput}; use crate::util::OptionTracker; use gtk::prelude::*; use relm4::prelude::*; pub struct SubtitleView { primary_cue: Controller, secondary_cue: OptionTracker, } #[derive(Debug)] pub enum SubtitleViewMsg { SetPrimaryCue(Option), SetSecondaryCue(Option), } #[derive(Debug)] pub enum SubtitleViewOutput { SetHoveringCue(bool), } #[relm4::component(pub)] impl SimpleComponent for SubtitleView { type Init = (); type Input = SubtitleViewMsg; type Output = SubtitleViewOutput; view! { gtk::ScrolledWindow { gtk::Box { set_orientation: gtk::Orientation::Vertical, set_vexpand: true, set_halign: gtk::Align::Center, gtk::Box { set_vexpand: true, }, model.primary_cue.widget(), gtk::Box { set_vexpand: true, }, gtk::Label { #[track = "model.secondary_cue.is_dirty()"] set_text: model.secondary_cue.get().as_ref().map(|val| val.as_str()).unwrap_or("TODO placeholder"), set_justify: gtk::Justification::Center, }, gtk::Box { set_vexpand: true, }, } }, } fn init( _init: Self::Init, root: Self::Root, sender: ComponentSender, ) -> ComponentParts { let model = Self { primary_cue: CueView::builder() .launch(()) .forward(sender.output_sender(), |output| match output { CueViewOutput::MouseEnter => SubtitleViewOutput::SetHoveringCue(true), CueViewOutput::MouseLeave => SubtitleViewOutput::SetHoveringCue(false), }), secondary_cue: OptionTracker::default(), }; let widgets = view_output!(); ComponentParts { model, widgets } } fn update(&mut self, msg: Self::Input, _sender: ComponentSender) { // Reset trackers self.secondary_cue.reset(); match msg { SubtitleViewMsg::SetPrimaryCue(value) => { self.primary_cue .sender() .send(CueViewMsg::SetText(value)) .unwrap(); } SubtitleViewMsg::SetSecondaryCue(value) => { self.secondary_cue.set(value); } } } }