mxl_player_components/ui/player/
model.rs

1use crate::player::{MaxLateness, Player, PlayerBuilder};
2use crate::ui::player::messages::PlaybackState;
3use log::*;
4use mxl_relm4_components::relm4::{gtk, gtk::prelude::*};
5use std::{rc::Rc, sync::Mutex};
6
7type DrawCallbackFn = dyn Fn(&gtk::cairo::Context, &mut VideoViewData);
8
9pub struct PlayerComponentInit {
10    pub seek_accurate: bool,
11    pub show_seeking_overlay: bool,
12    pub compositor: Option<gst::Element>,
13    pub qos: bool,
14    pub max_lateness: MaxLateness,
15    pub draw_callback: Option<Box<DrawCallbackFn>>,
16    pub drag_gesture: Option<gtk::GestureDrag>,
17    pub motion_tracker: Option<gtk::EventControllerMotion>,
18}
19
20impl Default for PlayerComponentInit {
21    fn default() -> Self {
22        Self {
23            seek_accurate: true,
24            show_seeking_overlay: false,
25            compositor: None,
26            qos: false,
27            max_lateness: Default::default(),
28            draw_callback: None,
29            drag_gesture: None,
30            motion_tracker: None,
31        }
32    }
33}
34
35#[derive(Debug, Default)]
36pub struct VideoViewData {
37    pub drawing_area: Option<gst_video::VideoRectangle>,
38    pub view_rect: Option<gst_video::VideoRectangle>,
39    pub video_dimensions: Option<gst_video::VideoRectangle>,
40    pub scaled_paintable_rect: Option<gst_video::VideoRectangle>,
41    pub fitted_paintable_rect: Option<gst_video::VideoRectangle>,
42    pub zoom_factor: f64,
43    pub(super) cursor_widgets: Vec<gtk::Widget>,
44    cursor_name: Option<String>,
45}
46
47#[derive(Debug, Default)]
48pub(super) struct ViewData {
49    pub(super) video_view: VideoViewData,
50}
51
52pub struct PlayerComponentModel {
53    pub(super) player_builder: PlayerBuilder,
54    pub(super) player: Option<Player>,
55    pub(super) playback_state: PlaybackState,
56    pub(super) show_seeking_overlay: bool,
57    pub(super) seeking: bool,
58    pub(super) show_drawing_overlay: bool,
59    pub(super) view_data: Rc<Mutex<ViewData>>,
60    pub(super) drag_position: Option<(f64, f64)>,
61    pub(super) mouse_position: Option<(f64, f64)>,
62}
63
64impl VideoViewData {
65    pub(super) fn set_cursor_widgets(&mut self, video_view: Vec<gtk::Widget>) {
66        self.cursor_widgets = video_view;
67    }
68
69    pub(super) fn set_cursor(&mut self, cursor_name: Option<&str>) {
70        debug!("Video view set cursor: {cursor_name:?}");
71        self.cursor_widgets.iter().for_each(|w| {
72            w.set_cursor_from_name(cursor_name);
73        });
74        self.cursor_name = cursor_name.map(str::to_string);
75    }
76
77    pub fn set_custom_cursor_from_name(&mut self, cursor_name: Option<&str>) {
78        if cursor_name.is_some() {
79            debug!("Video view set custom cursor: {cursor_name:?}");
80            self.cursor_widgets.iter().for_each(|w| {
81                w.set_cursor_from_name(cursor_name);
82            });
83        } else {
84            debug!("Video view reset current cursor: {:?}", self.cursor_name);
85            self.cursor_widgets.iter().for_each(|w| {
86                w.set_cursor_from_name(self.cursor_name.as_deref());
87            });
88        }
89    }
90
91    pub(super) fn update(
92        &mut self,
93        new_zoom_factor: Option<f64>,
94        video_scrolled_window: &gtk::ScrolledWindow,
95        video_picture: &gtk::Picture,
96    ) {
97        if let Some(new_zoom_factor) = new_zoom_factor {
98            self.zoom_factor = new_zoom_factor;
99        }
100
101        // Unscaled size of the video picture from GStreamer:
102        let p_width = video_picture.paintable().unwrap().intrinsic_width();
103        let p_height = video_picture.paintable().unwrap().intrinsic_height();
104
105        // let paintable_rect = gst_video::VideoRectangle::new(0, 0, p_width, p_height);
106        let paintable_rect = gst_video::VideoRectangle::new(
107            0,
108            0,
109            (p_width as f64 * self.zoom_factor) as i32,
110            (p_height as f64 * self.zoom_factor) as i32,
111        );
112        self.scaled_paintable_rect = Some(paintable_rect.clone());
113
114        let view_rect =
115            gst_video::VideoRectangle::new(0, 0, video_scrolled_window.width(), video_scrolled_window.height());
116
117        self.view_rect = Some(view_rect.clone());
118
119        if self.zoom_factor == 1.0 {
120            self.fitted_paintable_rect = Some(gst_video::center_video_rectangle(&paintable_rect, &view_rect, true));
121        } else {
122            trace!("paintable orig: p_width={p_width} p_height={p_height}");
123
124            let new_view_w = (view_rect.w as f64 * self.zoom_factor) as i32;
125            let new_view_h = (view_rect.h as f64 * self.zoom_factor) as i32;
126            let new_view_rect = gst_video::VideoRectangle::new(0, 0, new_view_w, new_view_h);
127
128            trace!("Zoomed view size: {new_view_rect:?}");
129
130            let unscaled_fitted_paintable_rect = gst_video::center_video_rectangle(&paintable_rect, &view_rect, true);
131
132            let new_paintable_w = (unscaled_fitted_paintable_rect.w as f64 * self.zoom_factor) as i32;
133            let new_paintable_h = (unscaled_fitted_paintable_rect.h as f64 * self.zoom_factor) as i32;
134            let fitted_paintable_rect = gst_video::VideoRectangle::new(0, 0, new_paintable_w, new_paintable_h);
135
136            let fitted_paintable_rect = gst_video::center_video_rectangle(&fitted_paintable_rect, &new_view_rect, true);
137
138            let fitted_paintable_rect = gst_video::VideoRectangle::new(
139                ((view_rect.w - fitted_paintable_rect.w) / 2).clamp(0, i32::MAX),
140                ((view_rect.h - fitted_paintable_rect.h) / 2).clamp(0, i32::MAX),
141                fitted_paintable_rect.w,
142                fitted_paintable_rect.h,
143            );
144
145            self.fitted_paintable_rect = Some(fitted_paintable_rect.clone());
146        }
147    }
148}