Release v0.2.7
This commit is contained in:
parent
be02626912
commit
f4a0c484ff
6 changed files with 273 additions and 65 deletions
301
src/viewer.rs
301
src/viewer.rs
|
|
@ -25,6 +25,7 @@ const IMAGE_ZOOM_MIN: f64 = 0.1;
|
|||
const IMAGE_ZOOM_MAX: f64 = 4.0;
|
||||
const VIEWER_CONTROLLER_KEY: &str = "comicviewer-viewer-controller";
|
||||
const MEDIA_VIEWER_CONTROLLER_KEY: &str = "comicviewer-media-viewer-controller";
|
||||
const PRESERVE_SEEK_CLASS: &str = "viewer-preserve-seek";
|
||||
|
||||
fn clear_viewer_controller(window: >k::ApplicationWindow) {
|
||||
unsafe {
|
||||
|
|
@ -110,10 +111,10 @@ pub(crate) fn set_viewer_header(
|
|||
pub struct ViewerWindow;
|
||||
|
||||
pub struct SiblingNavigator {
|
||||
position: Rc<dyn Fn() -> Option<(usize, usize)>>,
|
||||
navigate: Rc<dyn Fn(SiblingTarget, gtk::ApplicationWindow) -> bool>,
|
||||
files: Rc<dyn Fn() -> Vec<SiblingFile>>,
|
||||
actions: Rc<SiblingActionResolver>,
|
||||
recursive: bool,
|
||||
}
|
||||
|
||||
type SiblingActionResolver = dyn Fn(&str) -> Option<SiblingFileActions>;
|
||||
|
|
@ -133,16 +134,16 @@ pub struct SiblingFileActions {
|
|||
|
||||
impl SiblingNavigator {
|
||||
pub fn new(
|
||||
position: Rc<dyn Fn() -> Option<(usize, usize)>>,
|
||||
navigate: Rc<dyn Fn(SiblingTarget, gtk::ApplicationWindow) -> bool>,
|
||||
files: Rc<dyn Fn() -> Vec<SiblingFile>>,
|
||||
actions: Rc<SiblingActionResolver>,
|
||||
recursive: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
position,
|
||||
navigate,
|
||||
files,
|
||||
actions,
|
||||
recursive,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,14 +166,14 @@ impl SiblingNavigator {
|
|||
)
|
||||
}
|
||||
|
||||
fn position(&self) -> Option<(usize, usize)> {
|
||||
(self.position)()
|
||||
}
|
||||
|
||||
fn files(&self) -> Vec<SiblingFile> {
|
||||
(self.files)()
|
||||
}
|
||||
|
||||
fn seek_files(&self, current_kind: EntryKind) -> Vec<SiblingFile> {
|
||||
seek_sibling_files(self.files(), current_kind, self.recursive)
|
||||
}
|
||||
|
||||
fn open_uri(&self, uri: &str, window: gtk::ApplicationWindow) -> bool {
|
||||
let files = self.files();
|
||||
let Some(index) = files.iter().position(|file| file.uri == uri) else {
|
||||
|
|
@ -445,6 +446,7 @@ impl ViewerWindow {
|
|||
file_download: download_control.download,
|
||||
view_mode_change: view_mode_control.change,
|
||||
seek_hide_timer: RefCell::new(None),
|
||||
seek_interacting: Cell::new(false),
|
||||
last_pointer_position: Cell::new(None),
|
||||
key_controller: RefCell::new(None),
|
||||
motion_controller: RefCell::new(None),
|
||||
|
|
@ -477,6 +479,14 @@ impl ViewerWindow {
|
|||
});
|
||||
controller.connect_signals();
|
||||
controller.fullscreen_changed();
|
||||
if controller.window.has_css_class(PRESERVE_SEEK_CLASS) {
|
||||
controller.window.remove_css_class(PRESERVE_SEEK_CLASS);
|
||||
if controller.window.is_fullscreen() {
|
||||
controller.show_fullscreen_ui_temporarily();
|
||||
} else {
|
||||
controller.show_seek_temporarily();
|
||||
}
|
||||
}
|
||||
if controller.file_list_button.is_active() {
|
||||
controller.refresh_file_list();
|
||||
}
|
||||
|
|
@ -604,6 +614,7 @@ struct ViewerController {
|
|||
file_download: Rc<dyn Fn(gtk::Window, gtk::Label)>,
|
||||
view_mode_change: Rc<dyn Fn(u32)>,
|
||||
seek_hide_timer: RefCell<Option<glib::SourceId>>,
|
||||
seek_interacting: Cell<bool>,
|
||||
last_pointer_position: Cell<Option<(f64, f64)>>,
|
||||
key_controller: RefCell<Option<gtk::EventControllerKey>>,
|
||||
motion_controller: RefCell<Option<gtk::EventControllerMotion>>,
|
||||
|
|
@ -629,13 +640,13 @@ impl ViewerController {
|
|||
let controller = Rc::downgrade(self);
|
||||
self.previous_button.connect_clicked(move |_| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.navigate(false);
|
||||
controller.navigate_from_button(false);
|
||||
}
|
||||
});
|
||||
let controller = Rc::downgrade(self);
|
||||
self.next_button.connect_clicked(move |_| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.navigate(true);
|
||||
controller.navigate_from_button(true);
|
||||
}
|
||||
});
|
||||
let controller = Rc::downgrade(self);
|
||||
|
|
@ -740,6 +751,29 @@ impl ViewerController {
|
|||
controller.seek_page(index);
|
||||
}
|
||||
});
|
||||
let seek_click = gtk::GestureClick::builder()
|
||||
.button(gdk::BUTTON_PRIMARY)
|
||||
.propagation_phase(gtk::PropagationPhase::Capture)
|
||||
.build();
|
||||
let controller = Rc::downgrade(self);
|
||||
seek_click.connect_pressed(move |_, _, _, _| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.begin_seek_interaction();
|
||||
}
|
||||
});
|
||||
let controller = Rc::downgrade(self);
|
||||
seek_click.connect_released(move |_, _, _, _| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.end_seek_interaction();
|
||||
}
|
||||
});
|
||||
let controller = Rc::downgrade(self);
|
||||
seek_click.connect_cancel(move |_, _| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.end_seek_interaction();
|
||||
}
|
||||
});
|
||||
self.page_seek.add_controller(seek_click);
|
||||
|
||||
let adjustment = self.continuous_scrolled.vadjustment();
|
||||
let controller = Rc::downgrade(self);
|
||||
|
|
@ -1103,12 +1137,36 @@ impl ViewerController {
|
|||
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);
|
||||
if !controller.seek_interacting.get() {
|
||||
controller.set_seek_visible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
self.seek_hide_timer.replace(Some(timer));
|
||||
}
|
||||
|
||||
fn begin_seek_interaction(self: &Rc<Self>) {
|
||||
self.seek_interacting.set(true);
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.cancel_fullscreen_ui_timer();
|
||||
if self.window.is_fullscreen() {
|
||||
self.set_fullscreen_ui_visible(true);
|
||||
} else {
|
||||
self.set_seek_visible(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn end_seek_interaction(self: &Rc<Self>) {
|
||||
self.seek_interacting.set(false);
|
||||
if self.window.is_fullscreen() {
|
||||
self.show_fullscreen_ui_temporarily();
|
||||
} else {
|
||||
self.show_seek_temporarily();
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_moved(self: &Rc<Self>, x: f64, y: f64) {
|
||||
if self.last_pointer_position.get() == Some((x, y)) {
|
||||
return;
|
||||
|
|
@ -1128,7 +1186,9 @@ impl ViewerController {
|
|||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.set_seek_visible(false);
|
||||
if !self.seek_interacting.get() {
|
||||
self.set_seek_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_seek_visible(self: &Rc<Self>, visible: bool) {
|
||||
|
|
@ -1187,7 +1247,7 @@ impl ViewerController {
|
|||
}
|
||||
|
||||
fn hide_fullscreen_ui_now(self: &Rc<Self>) {
|
||||
if self.window.is_fullscreen() {
|
||||
if self.window.is_fullscreen() && !self.seek_interacting.get() {
|
||||
self.cancel_fullscreen_ui_timer();
|
||||
self.set_fullscreen_ui_visible(false);
|
||||
}
|
||||
|
|
@ -1442,7 +1502,13 @@ impl ViewerController {
|
|||
});
|
||||
}
|
||||
|
||||
fn navigate(self: &Rc<Self>, forward: bool) {
|
||||
fn navigate_from_button(self: &Rc<Self>, forward: bool) {
|
||||
if self.navigate(forward) && (self.image_zoom.replace(1.0) - 1.0).abs() >= f64::EPSILON {
|
||||
self.apply_view_mode();
|
||||
}
|
||||
}
|
||||
|
||||
fn navigate(self: &Rc<Self>, forward: bool) -> bool {
|
||||
let step = if self.view_mode() == ViewMode::TwoPage {
|
||||
2
|
||||
} else {
|
||||
|
|
@ -1450,7 +1516,7 @@ impl ViewerController {
|
|||
};
|
||||
let mut state = self.state.borrow_mut();
|
||||
if state.pages.is_empty() {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
let next = if forward {
|
||||
state.anchor.saturating_add(step).min(state.pages.len() - 1)
|
||||
|
|
@ -1458,7 +1524,7 @@ impl ViewerController {
|
|||
state.anchor.saturating_sub(step)
|
||||
};
|
||||
if next == state.anchor {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
state.anchor = next;
|
||||
drop(state);
|
||||
|
|
@ -1467,6 +1533,7 @@ impl ViewerController {
|
|||
if self.view_mode() == ViewMode::Continuous {
|
||||
self.schedule_scroll_to_anchor();
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn seek_edge(self: &Rc<Self>, end: bool) {
|
||||
|
|
@ -2175,10 +2242,12 @@ impl MediaViewerWindow {
|
|||
.build();
|
||||
let media_seek = navigation_scale();
|
||||
let media_seek_row = navigation_scale_row(&media_seek);
|
||||
if let Some((current, count)) = sibling_navigator
|
||||
let seek_files = sibling_navigator
|
||||
.as_ref()
|
||||
.and_then(SiblingNavigator::position)
|
||||
{
|
||||
.map(|navigator| navigator.seek_files(kind))
|
||||
.unwrap_or_default();
|
||||
if let Some(current) = seek_position(&seek_files, &source.source_uri) {
|
||||
let count = seek_files.len();
|
||||
media_seek.set_range(0.0, navigation_scale_upper(count));
|
||||
media_seek.set_value(current as f64);
|
||||
media_seek.set_sensitive(count > 1);
|
||||
|
|
@ -2219,6 +2288,7 @@ impl MediaViewerWindow {
|
|||
status_label,
|
||||
media_seek,
|
||||
media_seek_row,
|
||||
seek_files: RefCell::new(seek_files),
|
||||
sibling_navigator,
|
||||
file_list_button,
|
||||
file_list_panel: file_list.panel,
|
||||
|
|
@ -2231,6 +2301,8 @@ impl MediaViewerWindow {
|
|||
seek_generation: Cell::new(0),
|
||||
seek_opening: Cell::new(false),
|
||||
seek_hide_timer: RefCell::new(None),
|
||||
seek_interacting: Cell::new(false),
|
||||
pending_seek_index: Cell::new(None),
|
||||
last_pointer_position: Cell::new(None),
|
||||
key_controller: RefCell::new(None),
|
||||
motion_controller: RefCell::new(None),
|
||||
|
|
@ -2253,6 +2325,14 @@ impl MediaViewerWindow {
|
|||
}
|
||||
controller.connect_signals();
|
||||
controller.fullscreen_changed();
|
||||
if controller.window.has_css_class(PRESERVE_SEEK_CLASS) {
|
||||
controller.window.remove_css_class(PRESERVE_SEEK_CLASS);
|
||||
if controller.window.is_fullscreen() {
|
||||
controller.show_fullscreen_ui_temporarily();
|
||||
} else {
|
||||
controller.show_seek_temporarily();
|
||||
}
|
||||
}
|
||||
if controller.file_list_button.is_active() {
|
||||
controller.refresh_file_list();
|
||||
}
|
||||
|
|
@ -2294,6 +2374,7 @@ struct MediaViewerController {
|
|||
status_label: gtk::Label,
|
||||
media_seek: gtk::Scale,
|
||||
media_seek_row: gtk::Box,
|
||||
seek_files: RefCell<Vec<SiblingFile>>,
|
||||
sibling_navigator: Option<SiblingNavigator>,
|
||||
file_list_button: gtk::ToggleButton,
|
||||
file_list_panel: gtk::ScrolledWindow,
|
||||
|
|
@ -2306,6 +2387,8 @@ struct MediaViewerController {
|
|||
seek_generation: Cell<u64>,
|
||||
seek_opening: Cell<bool>,
|
||||
seek_hide_timer: RefCell<Option<glib::SourceId>>,
|
||||
seek_interacting: Cell<bool>,
|
||||
pending_seek_index: Cell<Option<usize>>,
|
||||
last_pointer_position: Cell<Option<(f64, f64)>>,
|
||||
key_controller: RefCell<Option<gtk::EventControllerKey>>,
|
||||
motion_controller: RefCell<Option<gtk::EventControllerMotion>>,
|
||||
|
|
@ -2589,17 +2672,39 @@ impl MediaViewerController {
|
|||
let Some(controller) = controller.upgrade() else {
|
||||
return;
|
||||
};
|
||||
let Some(navigator) = &controller.sibling_navigator else {
|
||||
return;
|
||||
};
|
||||
let Some((_, count)) = navigator.position() else {
|
||||
return;
|
||||
};
|
||||
let count = controller.seek_files.borrow().len();
|
||||
let Some(index) = page_seek_index(scale.value(), count) else {
|
||||
return;
|
||||
};
|
||||
controller.schedule_seek(index);
|
||||
if controller.seek_interacting.get() {
|
||||
controller.pending_seek_index.set(Some(index));
|
||||
} else {
|
||||
controller.schedule_seek(index);
|
||||
}
|
||||
});
|
||||
let seek_click = gtk::GestureClick::builder()
|
||||
.button(gdk::BUTTON_PRIMARY)
|
||||
.propagation_phase(gtk::PropagationPhase::Capture)
|
||||
.build();
|
||||
let controller = Rc::downgrade(self);
|
||||
seek_click.connect_pressed(move |_, _, _, _| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.begin_seek_interaction();
|
||||
}
|
||||
});
|
||||
let controller = Rc::downgrade(self);
|
||||
seek_click.connect_released(move |_, _, _, _| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.end_seek_interaction();
|
||||
}
|
||||
});
|
||||
let controller = Rc::downgrade(self);
|
||||
seek_click.connect_cancel(move |_, _| {
|
||||
if let Some(controller) = controller.upgrade() {
|
||||
controller.cancel_seek_interaction();
|
||||
}
|
||||
});
|
||||
self.media_seek.add_controller(seek_click);
|
||||
}
|
||||
|
||||
fn refresh_file_list(&self) {
|
||||
|
|
@ -2849,11 +2954,9 @@ impl MediaViewerController {
|
|||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
if let Some((current, count)) = self
|
||||
.sibling_navigator
|
||||
.as_ref()
|
||||
.and_then(SiblingNavigator::position)
|
||||
{
|
||||
let seek_files = self.seek_files.borrow();
|
||||
if let Some(current) = seek_position(&seek_files, &self.source.source_uri) {
|
||||
let count = seek_files.len();
|
||||
self.media_seek
|
||||
.set_range(0.0, navigation_scale_upper(count));
|
||||
self.media_seek.set_sensitive(count > 1);
|
||||
|
|
@ -2864,12 +2967,55 @@ impl MediaViewerController {
|
|||
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);
|
||||
if !controller.seek_interacting.get() {
|
||||
controller.set_seek_visible(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
self.seek_hide_timer.replace(Some(timer));
|
||||
}
|
||||
|
||||
fn begin_seek_interaction(self: &Rc<Self>) {
|
||||
self.seek_interacting.set(true);
|
||||
self.pending_seek_index.set(None);
|
||||
self.seek_generation
|
||||
.set(self.seek_generation.get().wrapping_add(1));
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.cancel_fullscreen_ui_timer();
|
||||
if self.window.is_fullscreen() {
|
||||
self.set_fullscreen_ui_visible(true);
|
||||
} else {
|
||||
self.set_seek_visible(true);
|
||||
}
|
||||
}
|
||||
|
||||
fn end_seek_interaction(self: &Rc<Self>) {
|
||||
if !self.seek_interacting.replace(false) {
|
||||
return;
|
||||
}
|
||||
if self.window.is_fullscreen() {
|
||||
self.show_fullscreen_ui_temporarily();
|
||||
} else {
|
||||
self.show_seek_temporarily();
|
||||
}
|
||||
if let Some(index) = self.pending_seek_index.take() {
|
||||
self.schedule_seek(index);
|
||||
}
|
||||
}
|
||||
|
||||
fn cancel_seek_interaction(self: &Rc<Self>) {
|
||||
self.pending_seek_index.set(None);
|
||||
if self.seek_interacting.replace(false) {
|
||||
if self.window.is_fullscreen() {
|
||||
self.show_fullscreen_ui_temporarily();
|
||||
} else {
|
||||
self.show_seek_temporarily();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pointer_moved(self: &Rc<Self>, x: f64, y: f64) {
|
||||
if self.last_pointer_position.get() == Some((x, y)) {
|
||||
return;
|
||||
|
|
@ -2889,7 +3035,9 @@ impl MediaViewerController {
|
|||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
}
|
||||
self.set_seek_visible(false);
|
||||
if !self.seek_interacting.get() {
|
||||
self.set_seek_visible(false);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_seek_visible(self: &Rc<Self>, visible: bool) {
|
||||
|
|
@ -2909,14 +3057,13 @@ impl MediaViewerController {
|
|||
}
|
||||
|
||||
fn schedule_seek(self: &Rc<Self>, index: usize) {
|
||||
let Some(navigator) = &self.sibling_navigator else {
|
||||
if self.sibling_navigator.is_none() {
|
||||
return;
|
||||
}
|
||||
let Some(file) = self.seek_files.borrow().get(index).cloned() else {
|
||||
return;
|
||||
};
|
||||
if navigator
|
||||
.position()
|
||||
.is_none_or(|(current, _)| index == current)
|
||||
|| self.seek_opening.get()
|
||||
{
|
||||
if file.uri == self.source.source_uri || self.seek_opening.get() {
|
||||
return;
|
||||
}
|
||||
let generation = self.seek_generation.get().wrapping_add(1);
|
||||
|
|
@ -2932,14 +3079,28 @@ impl MediaViewerController {
|
|||
let Some(navigator) = &controller.sibling_navigator else {
|
||||
return;
|
||||
};
|
||||
if navigator.open_index(index, controller.window.clone()) {
|
||||
controller.seek_opening.set(true);
|
||||
let Some(uri) = controller
|
||||
.seek_files
|
||||
.borrow()
|
||||
.get(index)
|
||||
.map(|file| file.uri.clone())
|
||||
else {
|
||||
return;
|
||||
};
|
||||
controller.seek_opening.set(true);
|
||||
controller.window.add_css_class(PRESERVE_SEEK_CLASS);
|
||||
if navigator.open_uri(&uri, controller.window.clone()) {
|
||||
controller.prepare_for_navigation();
|
||||
} else {
|
||||
controller.seek_opening.set(false);
|
||||
controller.window.remove_css_class(PRESERVE_SEEK_CLASS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn prepare_for_navigation(&self) {
|
||||
self.seek_generation
|
||||
.set(self.seek_generation.get().wrapping_add(1));
|
||||
self.stop_playback();
|
||||
if let Some(timer) = self.seek_hide_timer.borrow_mut().take() {
|
||||
timer.remove();
|
||||
|
|
@ -3008,7 +3169,7 @@ impl MediaViewerController {
|
|||
}
|
||||
|
||||
fn hide_fullscreen_ui_now(self: &Rc<Self>) {
|
||||
if self.window.is_fullscreen() {
|
||||
if self.window.is_fullscreen() && !self.seek_interacting.get() {
|
||||
self.cancel_fullscreen_ui_timer();
|
||||
self.set_fullscreen_ui_visible(false);
|
||||
}
|
||||
|
|
@ -3404,9 +3565,12 @@ fn single_picture_request(
|
|||
}
|
||||
|
||||
fn next_image_zoom(current: f64, zoom_in: bool) -> f64 {
|
||||
let direction = if zoom_in { 1.0 } else { -1.0 };
|
||||
((((current + direction * IMAGE_ZOOM_STEP) * 10.0).round()) / 10.0)
|
||||
.clamp(IMAGE_ZOOM_MIN, IMAGE_ZOOM_MAX)
|
||||
let multiplier = if zoom_in {
|
||||
1.0 + IMAGE_ZOOM_STEP
|
||||
} else {
|
||||
1.0 - IMAGE_ZOOM_STEP
|
||||
};
|
||||
((current * multiplier * 10_000.0).round() / 10_000.0).clamp(IMAGE_ZOOM_MIN, IMAGE_ZOOM_MAX)
|
||||
}
|
||||
|
||||
fn scaled_dimension(value: i32, zoom: f64) -> i32 {
|
||||
|
|
@ -3599,6 +3763,21 @@ fn sibling_index(current: usize, count: usize, forward: bool) -> Option<usize> {
|
|||
}
|
||||
}
|
||||
|
||||
fn seek_sibling_files(
|
||||
mut files: Vec<SiblingFile>,
|
||||
current_kind: EntryKind,
|
||||
recursive: bool,
|
||||
) -> Vec<SiblingFile> {
|
||||
if current_kind == EntryKind::Image && !recursive {
|
||||
files.retain(|file| file.kind != EntryKind::ZipArchive);
|
||||
}
|
||||
files
|
||||
}
|
||||
|
||||
fn seek_position(files: &[SiblingFile], current_uri: &str) -> Option<usize> {
|
||||
files.iter().position(|file| file.uri == current_uri)
|
||||
}
|
||||
|
||||
fn video_seek_target(timestamp: i64, duration: i64, delta: i64) -> i64 {
|
||||
let target = timestamp.saturating_add(delta).max(0);
|
||||
if duration > 0 {
|
||||
|
|
@ -3764,10 +3943,44 @@ mod tests {
|
|||
fn image_zoom_changes_by_ten_percent_and_clamps() {
|
||||
assert_eq!(next_image_zoom(1.0, true), 1.1);
|
||||
assert_eq!(next_image_zoom(1.0, false), 0.9);
|
||||
assert_eq!(next_image_zoom(1.1, true), 1.21);
|
||||
assert_eq!(next_image_zoom(1.1, false), 0.99);
|
||||
assert_eq!(next_image_zoom(IMAGE_ZOOM_MAX, true), IMAGE_ZOOM_MAX);
|
||||
assert_eq!(next_image_zoom(IMAGE_ZOOM_MIN, false), IMAGE_ZOOM_MIN);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ordinary_image_seek_excludes_zip_but_recursive_seek_keeps_it() {
|
||||
let files = vec![
|
||||
SiblingFile {
|
||||
uri: "file:///book.zip".into(),
|
||||
name: "book.zip".into(),
|
||||
kind: EntryKind::ZipArchive,
|
||||
},
|
||||
SiblingFile {
|
||||
uri: "file:///page.jpg".into(),
|
||||
name: "page.jpg".into(),
|
||||
kind: EntryKind::Image,
|
||||
},
|
||||
SiblingFile {
|
||||
uri: "file:///clip.mp4".into(),
|
||||
name: "clip.mp4".into(),
|
||||
kind: EntryKind::Video,
|
||||
},
|
||||
];
|
||||
|
||||
let ordinary = seek_sibling_files(files.clone(), EntryKind::Image, false);
|
||||
assert_eq!(ordinary.len(), 2);
|
||||
assert!(
|
||||
!ordinary
|
||||
.iter()
|
||||
.any(|file| file.kind == EntryKind::ZipArchive)
|
||||
);
|
||||
assert_eq!(seek_position(&ordinary, "file:///page.jpg"), Some(0));
|
||||
assert_eq!(seek_position(&ordinary, "file:///clip.mp4"), Some(1));
|
||||
assert_eq!(seek_sibling_files(files, EntryKind::Image, true).len(), 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn file_list_shortcut_rejects_command_modifiers() {
|
||||
assert!(!shortcut_modifiers_blocked(gdk::ModifierType::SHIFT_MASK));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue