mxl_player_components/ui/video_offsets_dialog/
widget.rs

1use crate::localization::helper::fl;
2use crate::ui::video_offsets_dialog::messages::{
3    VideoOffsetsComponentInput, VideoOffsetsComponentOutput, internal::PrivateMsg,
4};
5use crate::ui::video_offsets_dialog::model::{VideoOffsetsComponentInit, VideoOffsetsComponentModel};
6use mxl_relm4_components::relm4::{self, adw::prelude::*, prelude::*};
7
8const CONVERSION_RATE: f64 = 1000000_f64;
9const OFFSET_MIN: f64 = -1500_f64;
10const OFFSET_MAX: f64 = 1500_f64;
11const OFFSET_INCREMENT_STEP: f64 = 1_f64;
12const OFFSET_PAGE_INCREMENT: f64 = 100_f64;
13const OFFSET_PAGE_SIZE: f64 = 0_f64;
14
15#[relm4::component(pub)]
16impl Component for VideoOffsetsComponentModel {
17    type Init = VideoOffsetsComponentInit;
18    type Input = VideoOffsetsComponentInput;
19    type Output = VideoOffsetsComponentOutput;
20    type CommandOutput = ();
21
22    view! {
23        adw::PreferencesWindow {
24            set_title: Some(&fl!("video-offsets")),
25            set_hide_on_close: true,
26            set_destroy_with_parent: true,
27
28            add = &adw::PreferencesPage {
29                set_vexpand: true,
30
31                add = &adw::PreferencesGroup {
32                    adw::ActionRow {
33                        set_hexpand: true,
34                        set_title: &fl!("video-offsets-audio"),
35                        set_subtitle: &fl!("video-offsets-audio", "description"),
36
37                        add_suffix = &gtk::SpinButton {
38                            set_halign: gtk::Align::End,
39                            set_valign: gtk::Align::Center,
40                            #[watch]
41                            #[block_signal(audio_video_offset_changed_handler)]
42                            set_adjustment: &gtk::Adjustment::new(model.audio_video_offset as f64 / CONVERSION_RATE,
43                                                                  OFFSET_MIN,
44                                                                  OFFSET_MAX,
45                                                                  OFFSET_INCREMENT_STEP,
46                                                                  OFFSET_PAGE_INCREMENT,
47                                                                  OFFSET_PAGE_SIZE),
48                            connect_value_changed[sender] => move |spin_button| {
49                                sender
50                                    .input_sender()
51                                    .send(VideoOffsetsComponentInput::PrivateMessage(
52                                        PrivateMsg::AudioVideoOffsetChanged((spin_button.value() * CONVERSION_RATE) as i64),
53                                    ))
54                                    .unwrap_or_default();
55                            } @audio_video_offset_changed_handler,
56                        },
57                    },
58
59                    adw::ActionRow {
60                        set_hexpand: true,
61                        set_title: &fl!("video-offsets-subtitle"),
62                        set_subtitle: &fl!("video-offsets-subtitle", "description"),
63
64                        add_suffix = &gtk::SpinButton {
65                            set_halign: gtk::Align::End,
66                            set_valign: gtk::Align::Center,
67                            #[watch]
68                            #[block_signal(subtitle_video_offset_changed_handler)]
69                            set_adjustment: &gtk::Adjustment::new(model.subtitle_video_offset as f64 / CONVERSION_RATE,
70                                                                  OFFSET_MIN,
71                                                                  OFFSET_MAX,
72                                                                  OFFSET_INCREMENT_STEP,
73                                                                  OFFSET_PAGE_INCREMENT,
74                                                                  OFFSET_PAGE_SIZE),
75                            connect_value_changed[sender] => move |spin_button| {
76                                sender
77                                    .input_sender()
78                                    .send(VideoOffsetsComponentInput::PrivateMessage(
79                                        PrivateMsg::SubtitleVideoOffsetChanged((spin_button.value() * CONVERSION_RATE) as i64),
80                                    ))
81                                    .unwrap_or_default();
82                            } @subtitle_video_offset_changed_handler,
83                        },
84                    },
85                },
86            }
87        }
88    }
89
90    // Initialize the component.
91    fn init(init: Self::Init, root: Self::Root, sender: ComponentSender<Self>) -> ComponentParts<Self> {
92        let model = VideoOffsetsComponentModel {
93            audio_video_offset: init.audio_video_offset,
94            subtitle_video_offset: init.subtitle_video_offset,
95        };
96
97        let widgets = view_output!();
98
99        ComponentParts { model, widgets }
100    }
101
102    fn update_with_view(
103        &mut self,
104        widgets: &mut Self::Widgets,
105        msg: Self::Input,
106        sender: ComponentSender<Self>,
107        _: &Self::Root,
108    ) {
109        match msg {
110            VideoOffsetsComponentInput::SetAudioVideoOffset(offset) => self.audio_video_offset = offset,
111            VideoOffsetsComponentInput::SetSubtitleVideoOffset(offset) => self.subtitle_video_offset = offset,
112            VideoOffsetsComponentInput::PrivateMessage(msg) => match msg {
113                PrivateMsg::AudioVideoOffsetChanged(offset) => {
114                    sender
115                        .output(VideoOffsetsComponentOutput::SetAudioVideoOffset(offset))
116                        .unwrap_or_default();
117                }
118                PrivateMsg::SubtitleVideoOffsetChanged(offset) => {
119                    sender
120                        .output(VideoOffsetsComponentOutput::SetSubtitleVideoOffset(offset))
121                        .unwrap_or_default();
122                }
123            },
124        }
125        self.update_view(widgets, sender)
126    }
127}
128
129impl VideoOffsetsComponentModel {
130    pub fn audio_video_offset(&self) -> i64 {
131        self.audio_video_offset
132    }
133
134    pub fn subtitle_video_offset(&self) -> i64 {
135        self.subtitle_video_offset
136    }
137}