Reuse viewer windows and organize favorites
This commit is contained in:
parent
ef2a0a7915
commit
bb73c1abea
3 changed files with 587 additions and 163 deletions
408
src/viewer.rs
408
src/viewer.rs
|
|
@ -24,11 +24,15 @@ pub struct ViewerWindow;
|
|||
pub struct SiblingNavigator {
|
||||
current: usize,
|
||||
count: usize,
|
||||
open: Rc<dyn Fn(usize) -> bool>,
|
||||
open: Rc<dyn Fn(usize, gtk::ApplicationWindow) -> bool>,
|
||||
}
|
||||
|
||||
impl SiblingNavigator {
|
||||
pub fn new(current: usize, count: usize, open: Rc<dyn Fn(usize) -> bool>) -> Self {
|
||||
pub fn new(
|
||||
current: usize,
|
||||
count: usize,
|
||||
open: Rc<dyn Fn(usize, gtk::ApplicationWindow) -> bool>,
|
||||
) -> Self {
|
||||
Self {
|
||||
current,
|
||||
count,
|
||||
|
|
@ -36,15 +40,15 @@ impl SiblingNavigator {
|
|||
}
|
||||
}
|
||||
|
||||
fn open_relative(&self, forward: bool) -> bool {
|
||||
fn open_relative(&self, forward: bool, window: gtk::ApplicationWindow) -> bool {
|
||||
let Some(index) = sibling_index(self.current, self.count, forward) else {
|
||||
return false;
|
||||
};
|
||||
(self.open)(index)
|
||||
(self.open)(index, window)
|
||||
}
|
||||
|
||||
fn open_index(&self, index: usize) -> bool {
|
||||
index < self.count && index != self.current && (self.open)(index)
|
||||
fn open_index(&self, index: usize, window: gtk::ApplicationWindow) -> bool {
|
||||
index < self.count && index != self.current && (self.open)(index, window)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,23 +61,43 @@ pub struct FileDownloadControl {
|
|||
pub download: Rc<dyn Fn(gtk::Window, gtk::Label)>,
|
||||
}
|
||||
|
||||
pub struct ViewModeControl {
|
||||
pub selected: u32,
|
||||
pub change: Rc<dyn Fn(u32)>,
|
||||
}
|
||||
|
||||
pub struct FileViewerControls {
|
||||
pub sibling_navigator: Option<SiblingNavigator>,
|
||||
pub favorite_control: FavoriteControl,
|
||||
pub download_control: FileDownloadControl,
|
||||
pub reuse_window: Option<gtk::ApplicationWindow>,
|
||||
}
|
||||
|
||||
impl ViewerWindow {
|
||||
pub fn open(
|
||||
application: >k::Application,
|
||||
source: ArchiveSource,
|
||||
database: Rc<Database>,
|
||||
fast_image_loading: bool,
|
||||
sibling_navigator: Option<SiblingNavigator>,
|
||||
favorite_control: FavoriteControl,
|
||||
download_control: FileDownloadControl,
|
||||
view_mode_control: ViewModeControl,
|
||||
controls: FileViewerControls,
|
||||
) {
|
||||
let FileViewerControls {
|
||||
sibling_navigator,
|
||||
favorite_control,
|
||||
download_control,
|
||||
reuse_window,
|
||||
} = controls;
|
||||
let title = source.display_name.clone();
|
||||
let window = gtk::ApplicationWindow::builder()
|
||||
.application(application)
|
||||
.title(&title)
|
||||
.default_width(1100)
|
||||
.default_height(850)
|
||||
.build();
|
||||
let window = reuse_window.unwrap_or_else(|| {
|
||||
gtk::ApplicationWindow::builder()
|
||||
.application(application)
|
||||
.title(&title)
|
||||
.default_width(1100)
|
||||
.default_height(850)
|
||||
.build()
|
||||
});
|
||||
window.set_title(Some(&title));
|
||||
let header = gtk::HeaderBar::new();
|
||||
header.set_title_widget(Some(>k::Label::new(Some(&title))));
|
||||
let favorite_button = gtk::Button::with_label(favorite_label(favorite_control.active));
|
||||
|
|
@ -109,7 +133,7 @@ impl ViewerWindow {
|
|||
"2페이지",
|
||||
"이어서 보기",
|
||||
]);
|
||||
mode_dropdown.set_selected(1);
|
||||
mode_dropdown.set_selected(view_mode_control.selected);
|
||||
toolbar.append(&previous_button);
|
||||
toolbar.append(&shift_back_button);
|
||||
toolbar.append(&shift_forward_button);
|
||||
|
|
@ -159,14 +183,12 @@ impl ViewerWindow {
|
|||
.margin_start(8)
|
||||
.margin_end(8)
|
||||
.build();
|
||||
let page_seek = gtk::Scale::with_range(gtk::Orientation::Horizontal, 0.0, 0.0, 1.0);
|
||||
page_seek.set_draw_value(false);
|
||||
let page_seek = navigation_scale();
|
||||
let page_seek_row = navigation_scale_row(&page_seek);
|
||||
page_seek.set_sensitive(false);
|
||||
page_seek.set_margin_start(8);
|
||||
page_seek.set_margin_end(8);
|
||||
root.append(&toolbar);
|
||||
root.append(&overlay);
|
||||
root.append(&page_seek);
|
||||
root.append(&page_seek_row);
|
||||
root.append(&status_label);
|
||||
window.set_child(Some(&root));
|
||||
|
||||
|
|
@ -183,6 +205,7 @@ impl ViewerWindow {
|
|||
spinner,
|
||||
page_label,
|
||||
page_seek,
|
||||
page_seek_row,
|
||||
status_label,
|
||||
previous_button,
|
||||
next_button,
|
||||
|
|
@ -196,6 +219,11 @@ impl ViewerWindow {
|
|||
favorite_toggle: favorite_control.toggle,
|
||||
download_button,
|
||||
file_download: download_control.download,
|
||||
view_mode_change: view_mode_control.change,
|
||||
seek_hide_timer: RefCell::new(None),
|
||||
last_pointer_position: Cell::new(None),
|
||||
key_controller: RefCell::new(None),
|
||||
motion_controller: RefCell::new(None),
|
||||
state: RefCell::new(ViewerState {
|
||||
pages: Vec::new(),
|
||||
anchor: 0,
|
||||
|
|
@ -263,6 +291,7 @@ struct ViewerController {
|
|||
spinner: gtk::Spinner,
|
||||
page_label: gtk::Label,
|
||||
page_seek: gtk::Scale,
|
||||
page_seek_row: gtk::Box,
|
||||
status_label: gtk::Label,
|
||||
previous_button: gtk::Button,
|
||||
next_button: gtk::Button,
|
||||
|
|
@ -276,6 +305,11 @@ struct ViewerController {
|
|||
favorite_toggle: Rc<dyn Fn() -> bool>,
|
||||
download_button: gtk::Button,
|
||||
file_download: Rc<dyn Fn(gtk::Window, gtk::Label)>,
|
||||
view_mode_change: Rc<dyn Fn(u32)>,
|
||||
seek_hide_timer: RefCell<Option<glib::SourceId>>,
|
||||
last_pointer_position: Cell<Option<(f64, f64)>>,
|
||||
key_controller: RefCell<Option<gtk::EventControllerKey>>,
|
||||
motion_controller: RefCell<Option<gtk::EventControllerMotion>>,
|
||||
state: RefCell<ViewerState>,
|
||||
}
|
||||
|
||||
|
|
@ -294,8 +328,10 @@ impl ViewerController {
|
|||
self.shift_forward_button
|
||||
.connect_clicked(move |_| controller.shift_pairing(1));
|
||||
let controller = Rc::clone(self);
|
||||
self.mode_dropdown
|
||||
.connect_selected_notify(move |_| controller.apply_view_mode());
|
||||
self.mode_dropdown.connect_selected_notify(move |dropdown| {
|
||||
(controller.view_mode_change)(dropdown.selected());
|
||||
controller.apply_view_mode();
|
||||
});
|
||||
let controller = Rc::clone(self);
|
||||
self.direction_dropdown
|
||||
.connect_selected_notify(move |_| controller.render());
|
||||
|
|
@ -313,9 +349,8 @@ impl ViewerController {
|
|||
});
|
||||
let controller = Rc::clone(self);
|
||||
self.page_seek.connect_value_changed(move |scale| {
|
||||
if let Some(index) =
|
||||
page_seek_index(scale.value(), controller.state.borrow().pages.len())
|
||||
{
|
||||
let page_count = controller.state.borrow().pages.len();
|
||||
if let Some(index) = page_seek_index(scale.value(), page_count) {
|
||||
controller.seek_page(index);
|
||||
}
|
||||
});
|
||||
|
|
@ -345,49 +380,115 @@ impl ViewerController {
|
|||
});
|
||||
self.stack.add_controller(scroll_controller);
|
||||
|
||||
let motion_controller = gtk::EventControllerMotion::new();
|
||||
motion_controller.set_propagation_phase(gtk::PropagationPhase::Capture);
|
||||
let controller = Rc::clone(self);
|
||||
motion_controller.connect_motion(move |_, x, y| controller.pointer_moved(x, y));
|
||||
self.window.add_controller(motion_controller.clone());
|
||||
self.motion_controller.replace(Some(motion_controller));
|
||||
|
||||
let key_controller = gtk::EventControllerKey::new();
|
||||
let controller = Rc::clone(self);
|
||||
key_controller.connect_key_pressed(move |_, key, _, _| match key {
|
||||
gdk::Key::Left | gdk::Key::Page_Up => {
|
||||
controller.navigate(false);
|
||||
glib::Propagation::Stop
|
||||
key_controller.connect_key_pressed(move |_, key, _, _| {
|
||||
controller.hide_seek();
|
||||
match key {
|
||||
gdk::Key::Left | gdk::Key::Page_Up => {
|
||||
controller.navigate(false);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
gdk::Key::Right | gdk::Key::Page_Down | gdk::Key::space => {
|
||||
controller.navigate(true);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key.to_unicode() == Some('[') => {
|
||||
controller.open_sibling(false);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key.to_unicode() == Some(']') => {
|
||||
controller.open_sibling(true);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key
|
||||
.to_unicode()
|
||||
.is_some_and(|character| character.eq_ignore_ascii_case(&'f')) =>
|
||||
{
|
||||
controller.toggle_fullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
gdk::Key::Escape if controller.window.is_fullscreen() => {
|
||||
controller.window.unfullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ => glib::Propagation::Proceed,
|
||||
}
|
||||
gdk::Key::Right | gdk::Key::Page_Down | gdk::Key::space => {
|
||||
controller.navigate(true);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key.to_unicode() == Some('[') => {
|
||||
controller.open_sibling(false);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key.to_unicode() == Some(']') => {
|
||||
controller.open_sibling(true);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key
|
||||
.to_unicode()
|
||||
.is_some_and(|character| character.eq_ignore_ascii_case(&'f')) =>
|
||||
{
|
||||
controller.toggle_fullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
gdk::Key::Escape if controller.window.is_fullscreen() => {
|
||||
controller.window.unfullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ => glib::Propagation::Proceed,
|
||||
});
|
||||
self.window.add_controller(key_controller);
|
||||
self.window.add_controller(key_controller.clone());
|
||||
self.key_controller.replace(Some(key_controller));
|
||||
}
|
||||
|
||||
fn open_sibling(&self, forward: bool) {
|
||||
if let Some(navigate) = &self.sibling_navigator
|
||||
&& navigate.open_relative(forward)
|
||||
&& navigate.open_relative(forward, self.window.clone())
|
||||
{
|
||||
self.window.close();
|
||||
self.detach_window_controllers();
|
||||
}
|
||||
}
|
||||
|
||||
fn detach_window_controllers(&self) {
|
||||
if let Some(controller) = self.key_controller.borrow_mut().take() {
|
||||
self.window.remove_controller(&controller);
|
||||
}
|
||||
if let Some(controller) = self.motion_controller.borrow_mut().take() {
|
||||
self.window.remove_controller(&controller);
|
||||
}
|
||||
}
|
||||
|
||||
fn show_seek_temporarily(self: &Rc<Self>) {
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.set_seek_visible(true);
|
||||
let controller = Rc::downgrade(self);
|
||||
let timer = glib::timeout_add_local_once(Duration::from_secs(5), move || {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.seek_hide_timer.borrow_mut().take();
|
||||
controller.set_seek_visible(false);
|
||||
}
|
||||
});
|
||||
self.seek_hide_timer.replace(Some(timer));
|
||||
}
|
||||
|
||||
fn pointer_moved(self: &Rc<Self>, x: f64, y: f64) {
|
||||
if self.last_pointer_position.get() == Some((x, y)) {
|
||||
return;
|
||||
}
|
||||
self.last_pointer_position.set(Some((x, y)));
|
||||
self.show_seek_temporarily();
|
||||
}
|
||||
|
||||
fn hide_seek(self: &Rc<Self>) {
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.set_seek_visible(false);
|
||||
}
|
||||
|
||||
fn set_seek_visible(self: &Rc<Self>, visible: bool) {
|
||||
if self.page_seek_row.is_visible() == visible {
|
||||
return;
|
||||
}
|
||||
self.page_seek_row.set_visible(visible);
|
||||
self.window.queue_allocate();
|
||||
let controller = Rc::downgrade(self);
|
||||
glib::idle_add_local_once(move || {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.stack.queue_allocate();
|
||||
controller.resize_continuous_rows();
|
||||
controller.render();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn toggle_fullscreen(&self) {
|
||||
if self.window.is_fullscreen() {
|
||||
self.window.unfullscreen();
|
||||
|
|
@ -438,7 +539,8 @@ impl ViewerController {
|
|||
state.pages = pages;
|
||||
state.anchor = saved;
|
||||
drop(state);
|
||||
self.page_seek.set_range(0.0, (page_count - 1) as f64);
|
||||
self.page_seek
|
||||
.set_range(0.0, navigation_scale_upper(page_count));
|
||||
self.page_seek.set_sensitive(page_count > 1);
|
||||
self.build_continuous_rows();
|
||||
self.render();
|
||||
|
|
@ -986,17 +1088,24 @@ impl MediaViewerWindow {
|
|||
application: >k::Application,
|
||||
source: ArchiveSource,
|
||||
kind: EntryKind,
|
||||
sibling_navigator: Option<SiblingNavigator>,
|
||||
favorite_control: FavoriteControl,
|
||||
download_control: FileDownloadControl,
|
||||
controls: FileViewerControls,
|
||||
) {
|
||||
let FileViewerControls {
|
||||
sibling_navigator,
|
||||
favorite_control,
|
||||
download_control,
|
||||
reuse_window,
|
||||
} = controls;
|
||||
let title = source.display_name.clone();
|
||||
let window = gtk::ApplicationWindow::builder()
|
||||
.application(application)
|
||||
.title(&title)
|
||||
.default_width(1100)
|
||||
.default_height(850)
|
||||
.build();
|
||||
let window = reuse_window.unwrap_or_else(|| {
|
||||
gtk::ApplicationWindow::builder()
|
||||
.application(application)
|
||||
.title(&title)
|
||||
.default_width(1100)
|
||||
.default_height(850)
|
||||
.build()
|
||||
});
|
||||
window.set_title(Some(&title));
|
||||
let header = gtk::HeaderBar::new();
|
||||
header.set_title_widget(Some(>k::Label::new(Some(&title))));
|
||||
let favorite_button = gtk::Button::with_label(favorite_label(favorite_control.active));
|
||||
|
|
@ -1028,19 +1137,17 @@ impl MediaViewerWindow {
|
|||
.margin_start(8)
|
||||
.margin_end(8)
|
||||
.build();
|
||||
let media_seek = gtk::Scale::with_range(gtk::Orientation::Horizontal, 0.0, 0.0, 1.0);
|
||||
media_seek.set_draw_value(false);
|
||||
media_seek.set_margin_start(8);
|
||||
media_seek.set_margin_end(8);
|
||||
let media_seek = navigation_scale();
|
||||
let media_seek_row = navigation_scale_row(&media_seek);
|
||||
if let Some(navigator) = &sibling_navigator {
|
||||
media_seek.set_range(0.0, navigator.count.saturating_sub(1) as f64);
|
||||
media_seek.set_range(0.0, navigation_scale_upper(navigator.count));
|
||||
media_seek.set_value(navigator.current as f64);
|
||||
media_seek.set_sensitive(navigator.count > 1);
|
||||
} else {
|
||||
media_seek.set_sensitive(false);
|
||||
}
|
||||
root.append(&stack);
|
||||
root.append(&media_seek);
|
||||
root.append(&media_seek_row);
|
||||
root.append(&status_label);
|
||||
window.set_child(Some(&root));
|
||||
|
||||
|
|
@ -1052,6 +1159,7 @@ impl MediaViewerWindow {
|
|||
stack,
|
||||
status_label,
|
||||
media_seek,
|
||||
media_seek_row,
|
||||
sibling_navigator,
|
||||
favorite_button,
|
||||
favorite_toggle: favorite_control.toggle,
|
||||
|
|
@ -1059,6 +1167,10 @@ impl MediaViewerWindow {
|
|||
file_download: download_control.download,
|
||||
seek_generation: Cell::new(0),
|
||||
seek_opening: Cell::new(false),
|
||||
seek_hide_timer: RefCell::new(None),
|
||||
last_pointer_position: Cell::new(None),
|
||||
key_controller: RefCell::new(None),
|
||||
motion_controller: RefCell::new(None),
|
||||
});
|
||||
if let Some(stream) = controller.video.media_stream() {
|
||||
let status_label = controller.status_label.clone();
|
||||
|
|
@ -1091,6 +1203,7 @@ struct MediaViewerController {
|
|||
stack: gtk::Stack,
|
||||
status_label: gtk::Label,
|
||||
media_seek: gtk::Scale,
|
||||
media_seek_row: gtk::Box,
|
||||
sibling_navigator: Option<SiblingNavigator>,
|
||||
favorite_button: gtk::Button,
|
||||
favorite_toggle: Rc<dyn Fn() -> bool>,
|
||||
|
|
@ -1098,32 +1211,47 @@ struct MediaViewerController {
|
|||
file_download: Rc<dyn Fn(gtk::Window, gtk::Label)>,
|
||||
seek_generation: Cell<u64>,
|
||||
seek_opening: Cell<bool>,
|
||||
seek_hide_timer: RefCell<Option<glib::SourceId>>,
|
||||
last_pointer_position: Cell<Option<(f64, f64)>>,
|
||||
key_controller: RefCell<Option<gtk::EventControllerKey>>,
|
||||
motion_controller: RefCell<Option<gtk::EventControllerMotion>>,
|
||||
}
|
||||
|
||||
impl MediaViewerController {
|
||||
fn connect_signals(self: &Rc<Self>) {
|
||||
let motion_controller = gtk::EventControllerMotion::new();
|
||||
motion_controller.set_propagation_phase(gtk::PropagationPhase::Capture);
|
||||
let controller = Rc::clone(self);
|
||||
motion_controller.connect_motion(move |_, x, y| controller.pointer_moved(x, y));
|
||||
self.window.add_controller(motion_controller.clone());
|
||||
self.motion_controller.replace(Some(motion_controller));
|
||||
|
||||
let keys = gtk::EventControllerKey::new();
|
||||
let controller = Rc::clone(self);
|
||||
keys.connect_key_pressed(move |_, key, _, _| match key.to_unicode() {
|
||||
Some('[') => {
|
||||
controller.open_sibling(false);
|
||||
glib::Propagation::Stop
|
||||
keys.connect_key_pressed(move |_, key, _, _| {
|
||||
controller.hide_seek();
|
||||
match key.to_unicode() {
|
||||
Some('[') => {
|
||||
controller.open_sibling(false);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
Some(']') => {
|
||||
controller.open_sibling(true);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
Some(character) if character.eq_ignore_ascii_case(&'f') => {
|
||||
controller.toggle_fullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key == gdk::Key::Escape && controller.window.is_fullscreen() => {
|
||||
controller.window.unfullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ => glib::Propagation::Proceed,
|
||||
}
|
||||
Some(']') => {
|
||||
controller.open_sibling(true);
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
Some(character) if character.eq_ignore_ascii_case(&'f') => {
|
||||
controller.toggle_fullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ if key == gdk::Key::Escape && controller.window.is_fullscreen() => {
|
||||
controller.window.unfullscreen();
|
||||
glib::Propagation::Stop
|
||||
}
|
||||
_ => glib::Propagation::Proceed,
|
||||
});
|
||||
self.window.add_controller(keys);
|
||||
self.window.add_controller(keys.clone());
|
||||
self.key_controller.replace(Some(keys));
|
||||
let controller = Rc::clone(self);
|
||||
self.favorite_button.connect_clicked(move |_| {
|
||||
let active = (controller.favorite_toggle)();
|
||||
|
|
@ -1150,12 +1278,58 @@ impl MediaViewerController {
|
|||
|
||||
fn open_sibling(&self, forward: bool) {
|
||||
if let Some(navigate) = &self.sibling_navigator
|
||||
&& navigate.open_relative(forward)
|
||||
&& navigate.open_relative(forward, self.window.clone())
|
||||
{
|
||||
self.close_for_navigation();
|
||||
self.prepare_for_navigation();
|
||||
}
|
||||
}
|
||||
|
||||
fn show_seek_temporarily(self: &Rc<Self>) {
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.set_seek_visible(true);
|
||||
let controller = Rc::downgrade(self);
|
||||
let timer = glib::timeout_add_local_once(Duration::from_secs(5), move || {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.seek_hide_timer.borrow_mut().take();
|
||||
controller.set_seek_visible(false);
|
||||
}
|
||||
});
|
||||
self.seek_hide_timer.replace(Some(timer));
|
||||
}
|
||||
|
||||
fn pointer_moved(self: &Rc<Self>, x: f64, y: f64) {
|
||||
if self.last_pointer_position.get() == Some((x, y)) {
|
||||
return;
|
||||
}
|
||||
self.last_pointer_position.set(Some((x, y)));
|
||||
self.show_seek_temporarily();
|
||||
}
|
||||
|
||||
fn hide_seek(self: &Rc<Self>) {
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.set_seek_visible(false);
|
||||
}
|
||||
|
||||
fn set_seek_visible(self: &Rc<Self>, visible: bool) {
|
||||
if self.media_seek_row.is_visible() == visible {
|
||||
return;
|
||||
}
|
||||
self.media_seek_row.set_visible(visible);
|
||||
self.window.queue_allocate();
|
||||
let controller = Rc::downgrade(self);
|
||||
glib::idle_add_local_once(move || {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.stack.queue_allocate();
|
||||
controller.picture.queue_allocate();
|
||||
controller.video.queue_allocate();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn schedule_seek(self: &Rc<Self>, index: usize) {
|
||||
let Some(navigator) = &self.sibling_navigator else {
|
||||
return;
|
||||
|
|
@ -1176,18 +1350,23 @@ impl MediaViewerController {
|
|||
let Some(navigator) = &controller.sibling_navigator else {
|
||||
return;
|
||||
};
|
||||
if navigator.open_index(index) {
|
||||
if navigator.open_index(index, controller.window.clone()) {
|
||||
controller.seek_opening.set(true);
|
||||
controller.close_for_navigation();
|
||||
controller.prepare_for_navigation();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn close_for_navigation(&self) {
|
||||
fn prepare_for_navigation(&self) {
|
||||
if let Some(stream) = self.video.media_stream() {
|
||||
stream.pause();
|
||||
}
|
||||
self.window.close();
|
||||
if let Some(controller) = self.key_controller.borrow_mut().take() {
|
||||
self.window.remove_controller(&controller);
|
||||
}
|
||||
if let Some(controller) = self.motion_controller.borrow_mut().take() {
|
||||
self.window.remove_controller(&controller);
|
||||
}
|
||||
}
|
||||
|
||||
fn toggle_fullscreen(&self) {
|
||||
|
|
@ -1259,6 +1438,33 @@ fn page_picture() -> gtk::Picture {
|
|||
picture
|
||||
}
|
||||
|
||||
fn navigation_scale() -> gtk::Scale {
|
||||
let scale = gtk::Scale::with_range(gtk::Orientation::Horizontal, 0.0, 1.0, 1.0);
|
||||
scale.set_draw_value(false);
|
||||
scale.set_hexpand(true);
|
||||
scale.set_height_request(32);
|
||||
scale.set_valign(gtk::Align::Center);
|
||||
scale.set_focusable(false);
|
||||
scale.set_margin_top(2);
|
||||
scale.set_margin_bottom(8);
|
||||
scale.set_margin_start(8);
|
||||
scale.set_margin_end(8);
|
||||
scale
|
||||
}
|
||||
|
||||
fn navigation_scale_row(scale: >k::Scale) -> gtk::Box {
|
||||
let row = gtk::Box::new(gtk::Orientation::Horizontal, 0);
|
||||
row.set_height_request(42);
|
||||
row.set_hexpand(true);
|
||||
row.append(scale);
|
||||
row.set_visible(false);
|
||||
row
|
||||
}
|
||||
|
||||
fn navigation_scale_upper(count: usize) -> f64 {
|
||||
count.saturating_sub(1).max(1) as f64
|
||||
}
|
||||
|
||||
fn spread_indices(anchor: usize, count: usize) -> Vec<usize> {
|
||||
if count == 0 {
|
||||
return Vec::new();
|
||||
|
|
@ -1370,6 +1576,14 @@ mod tests {
|
|||
assert_eq!(page_seek_index(0.0, 0), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn navigation_scale_always_has_a_valid_range() {
|
||||
assert_eq!(navigation_scale_upper(0), 1.0);
|
||||
assert_eq!(navigation_scale_upper(1), 1.0);
|
||||
assert_eq!(navigation_scale_upper(2), 1.0);
|
||||
assert_eq!(navigation_scale_upper(10), 9.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sibling_navigation_stops_at_boundaries() {
|
||||
assert_eq!(sibling_index(1, 3, false), Some(0));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue