Skip to main content

mimick/library/lightbox/
fullscreen.rs

1use std::cell::Cell;
2use std::rc::Rc;
3use std::time::{Duration, Instant};
4
5use gtk::prelude::*;
6
7use super::Lightbox;
8
9const HIDE_DELAY: Duration = Duration::from_secs(3);
10
11pub(super) struct Chrome {
12    hover: gtk::EventControllerMotion,
13    pointer: Cell<(f64, f64)>,
14    hide_at: Cell<Instant>,
15    armed: Cell<bool>,
16}
17
18impl Chrome {
19    pub(super) fn new() -> Self {
20        Self {
21            hover: gtk::EventControllerMotion::new(),
22            pointer: Cell::new((0.0, 0.0)),
23            hide_at: Cell::new(Instant::now()),
24            armed: Cell::new(false),
25        }
26    }
27}
28
29impl Lightbox {
30    pub(super) fn connect_fullscreen(self: &Rc<Self>) {
31        let lb = Rc::downgrade(self);
32        let handler = self.ui.window.connect_fullscreened_notify(move |window| {
33            if let Some(lb) = lb.upgrade() {
34                lb.apply_fullscreen(window.is_fullscreen());
35            }
36        });
37        *self.fullscreen_handler.borrow_mut() = Some(handler);
38        let lb = Rc::downgrade(self);
39        self.header.fullscreen_btn.connect_clicked(move |_| {
40            if let Some(lb) = lb.upgrade() {
41                lb.toggle_fullscreen();
42            }
43        });
44        self.header.bar.add_controller(self.chrome.hover.clone());
45        self.connect_chrome_reveal();
46    }
47
48    pub(super) fn toggle_fullscreen(&self) {
49        let window = &self.ui.window;
50        window.set_fullscreened(!window.is_fullscreen());
51    }
52
53    pub(super) fn close_fullscreen(&self) {
54        if let Some(handler) = self.fullscreen_handler.take() {
55            self.ui.window.disconnect(handler);
56        }
57        if self.ui.window.is_fullscreen() {
58            self.ui.window.unfullscreen();
59        }
60    }
61
62    fn apply_fullscreen(&self, fullscreen: bool) {
63        let toolbar = &self.header.toolbar;
64        toolbar.set_extend_content_to_top_edge(fullscreen);
65        toolbar.set_reveal_top_bars(!fullscreen);
66        self.actions.bar.set_visible(!fullscreen);
67        super::set_viewer_margins(&self.viewer, fullscreen);
68        if fullscreen {
69            self.viewer.add_css_class("mimick-fullscreen");
70            self.header.details_btn.set_active(false);
71        } else {
72            self.viewer.remove_css_class("mimick-fullscreen");
73        }
74        let (icon, tooltip) = if fullscreen {
75            ("view-restore-symbolic", "Exit fullscreen (F11)")
76        } else {
77            ("view-fullscreen-symbolic", "Fullscreen (F11)")
78        };
79        self.header.fullscreen_btn.set_icon_name(icon);
80        self.header.fullscreen_btn.set_tooltip_text(Some(tooltip));
81        if let Some(player) = self.video.borrow().as_ref() {
82            player.set_fullscreen(fullscreen);
83        }
84    }
85
86    fn connect_chrome_reveal(self: &Rc<Self>) {
87        let motion = gtk::EventControllerMotion::new();
88        let lb = Rc::downgrade(self);
89        motion.connect_motion(move |_, x, y| {
90            if let Some(lb) = lb.upgrade()
91                && lb.chrome.pointer.replace((x, y)) != (x, y)
92            {
93                lb.reveal_chrome();
94            }
95        });
96        self.picture.overlay.add_controller(motion);
97        let tap = gtk::GestureClick::new();
98        let lb = Rc::downgrade(self);
99        tap.connect_pressed(move |_, n_press, _, _| {
100            if n_press == 1
101                && let Some(lb) = lb.upgrade()
102            {
103                lb.reveal_chrome();
104            }
105        });
106        self.picture.overlay.add_controller(tap);
107    }
108
109    fn reveal_chrome(self: &Rc<Self>) {
110        if !self.ui.window.is_fullscreen() {
111            return;
112        }
113        self.header.toolbar.set_reveal_top_bars(true);
114        self.chrome.hide_at.set(Instant::now() + HIDE_DELAY);
115        if !self.chrome.armed.replace(true) {
116            self.arm_chrome_hide(HIDE_DELAY);
117        }
118    }
119
120    fn arm_chrome_hide(self: &Rc<Self>, delay: Duration) {
121        let lb = Rc::downgrade(self);
122        glib::timeout_add_local_once(delay, move || {
123            let Some(lb) = lb.upgrade() else {
124                return;
125            };
126            let remaining = lb
127                .chrome
128                .hide_at
129                .get()
130                .saturating_duration_since(Instant::now());
131            if !remaining.is_zero() {
132                lb.arm_chrome_hide(remaining);
133                return;
134            }
135            lb.chrome.armed.set(false);
136            if lb.ui.window.is_fullscreen() && !lb.chrome.hover.contains_pointer() {
137                lb.header.toolbar.set_reveal_top_bars(false);
138            }
139        });
140    }
141}