2020-12-26 02:05:34 -08:00
|
|
|
// image_view.rs
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2020 Christopher Davis <christopherdavis@gnome.org>
|
|
|
|
|
//
|
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
|
// it under the terms of the GNU General Public License as published by
|
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
// (at your option) any later version.
|
|
|
|
|
//
|
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
// GNU General Public License for more details.
|
|
|
|
|
//
|
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
//
|
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
|
2021-12-21 16:09:48 -08:00
|
|
|
use crate::deps::*;
|
|
|
|
|
|
2021-12-21 12:15:46 -08:00
|
|
|
use adw::subclass::prelude::*;
|
2020-12-26 02:05:34 -08:00
|
|
|
use glib::clone;
|
|
|
|
|
use gtk::prelude::*;
|
|
|
|
|
use gtk::subclass::prelude::*;
|
|
|
|
|
use gtk::CompositeTemplate;
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
use anyhow::{bail, Context};
|
2021-08-17 20:37:04 -07:00
|
|
|
use ashpd::desktop::wallpaper;
|
|
|
|
|
use ashpd::WindowIdentifier;
|
2021-01-30 15:52:32 -08:00
|
|
|
use once_cell::sync::Lazy;
|
2022-01-16 19:03:13 -08:00
|
|
|
use std::cell::RefCell;
|
2020-12-26 02:05:34 -08:00
|
|
|
|
2022-01-16 19:03:13 -08:00
|
|
|
use crate::file_model::LpFileModel;
|
2022-01-08 16:21:27 +01:00
|
|
|
use crate::thumbnail::Thumbnail;
|
2021-09-05 05:51:34 -07:00
|
|
|
use crate::util;
|
2022-01-19 23:11:38 -08:00
|
|
|
use crate::widgets::LpImagePage;
|
|
|
|
|
|
|
|
|
|
// Maximum number of pages to load
|
|
|
|
|
// at any given time
|
|
|
|
|
const N_PAGES: u32 = 3;
|
2021-09-05 05:51:34 -07:00
|
|
|
|
2020-12-26 02:05:34 -08:00
|
|
|
mod imp {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
2021-09-05 05:51:34 -07:00
|
|
|
#[derive(Debug, Default, CompositeTemplate)]
|
2021-09-28 17:44:26 -07:00
|
|
|
#[template(resource = "/org/gnome/Loupe/gtk/image_view.ui")]
|
2021-12-21 10:55:02 -08:00
|
|
|
pub struct LpImageView {
|
2020-12-26 02:05:34 -08:00
|
|
|
#[template_child]
|
2022-01-19 23:11:38 -08:00
|
|
|
pub carousel: TemplateChild<adw::Carousel>,
|
2021-09-05 05:51:34 -07:00
|
|
|
|
2022-01-16 19:03:13 -08:00
|
|
|
pub model: RefCell<Option<LpFileModel>>,
|
2022-01-19 23:11:38 -08:00
|
|
|
pub filename: RefCell<Option<String>>,
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
|
2021-06-27 15:28:35 -07:00
|
|
|
#[glib::object_subclass]
|
2021-12-21 10:55:02 -08:00
|
|
|
impl ObjectSubclass for LpImageView {
|
|
|
|
|
const NAME: &'static str = "LpImageView";
|
|
|
|
|
type Type = super::LpImageView;
|
2021-12-21 12:15:46 -08:00
|
|
|
type ParentType = adw::Bin;
|
2020-12-26 02:05:34 -08:00
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
2021-01-30 15:52:32 -08:00
|
|
|
Self::bind_template(klass);
|
2022-01-19 23:11:38 -08:00
|
|
|
Self::Type::bind_template_callbacks(klass);
|
2021-09-05 05:51:34 -07:00
|
|
|
|
|
|
|
|
klass.install_action("iv.next", None, move |image_view, _, _| {
|
2022-01-19 23:11:38 -08:00
|
|
|
image_view.navigate(adw::NavigationDirection::Forward);
|
2021-09-05 05:51:34 -07:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
klass.install_action("iv.previous", None, move |image_view, _, _| {
|
2022-01-19 23:11:38 -08:00
|
|
|
image_view.navigate(adw::NavigationDirection::Back);
|
2021-09-05 05:51:34 -07:00
|
|
|
});
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
2021-01-13 23:05:45 -08:00
|
|
|
|
2021-06-27 15:28:35 -07:00
|
|
|
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
2021-01-13 23:05:45 -08:00
|
|
|
obj.init_template();
|
|
|
|
|
}
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
|
2021-12-21 10:55:02 -08:00
|
|
|
impl ObjectImpl for LpImageView {
|
2021-01-30 15:52:32 -08:00
|
|
|
fn properties() -> &'static [glib::ParamSpec] {
|
|
|
|
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
2022-01-19 23:11:38 -08:00
|
|
|
vec![glib::ParamSpecString::new(
|
|
|
|
|
"filename",
|
|
|
|
|
"Filename",
|
|
|
|
|
"The filename of the current file",
|
|
|
|
|
None,
|
|
|
|
|
glib::ParamFlags::READABLE,
|
|
|
|
|
)]
|
2021-01-30 15:52:32 -08:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
PROPERTIES.as_ref()
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-16 19:03:13 -08:00
|
|
|
fn property(&self, obj: &Self::Type, _id: usize, psec: &glib::ParamSpec) -> glib::Value {
|
2021-06-27 15:28:35 -07:00
|
|
|
match psec.name() {
|
2022-01-16 19:03:13 -08:00
|
|
|
"filename" => obj.filename().to_value(),
|
2020-12-26 02:05:34 -08:00
|
|
|
_ => unimplemented!(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-01-30 15:52:32 -08:00
|
|
|
fn constructed(&self, obj: &Self::Type) {
|
|
|
|
|
self.parent_constructed(obj);
|
|
|
|
|
|
2022-01-08 16:21:27 +01:00
|
|
|
let source = gtk::DragSource::new();
|
|
|
|
|
|
|
|
|
|
source.connect_prepare(
|
|
|
|
|
glib::clone!(@weak obj => @default-return None, move |_, _, _| {
|
2022-01-19 23:11:38 -08:00
|
|
|
obj.current_page().map(|p| p.content_provider())
|
2022-01-08 16:21:27 +01:00
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
source.connect_drag_begin(glib::clone!(@weak obj => move |source, _| {
|
2022-01-19 23:11:38 -08:00
|
|
|
if let Some(texture) = obj.current_page().and_then(|p| p.texture()) {
|
2022-01-08 16:21:27 +01:00
|
|
|
let thumbnail = Thumbnail::new(&texture);
|
|
|
|
|
source.set_icon(Some(&thumbnail), 0, 0);
|
|
|
|
|
};
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
obj.add_controller(&source);
|
2021-01-30 15:52:32 -08:00
|
|
|
}
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
|
2021-12-21 10:55:02 -08:00
|
|
|
impl WidgetImpl for LpImageView {}
|
|
|
|
|
impl BinImpl for LpImageView {}
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glib::wrapper! {
|
2021-12-21 10:55:02 -08:00
|
|
|
pub struct LpImageView(ObjectSubclass<imp::LpImageView>)
|
2021-12-21 12:15:46 -08:00
|
|
|
@extends gtk::Widget, adw::Bin,
|
2020-12-26 02:05:34 -08:00
|
|
|
@implements gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
#[gtk::template_callbacks]
|
2021-12-21 10:55:02 -08:00
|
|
|
impl LpImageView {
|
2022-01-19 23:11:38 -08:00
|
|
|
pub fn set_image_from_file(&self, file: &gio::File) -> anyhow::Result<()> {
|
|
|
|
|
if let Some(current_file) = self.current_page().and_then(|p| p.file()) {
|
|
|
|
|
if current_file.equal(file) {
|
|
|
|
|
bail!("Image is the same as the previous image; Doing nothing.");
|
2021-09-05 05:51:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
self.build_model_from_file(file);
|
|
|
|
|
self.update_action_state(file);
|
2021-09-05 05:51:34 -07:00
|
|
|
self.notify("filename");
|
2021-03-02 16:39:03 -08:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
// TODO: rework width stuff
|
|
|
|
|
// let width = imp.picture.image_width();
|
|
|
|
|
// let height = imp.picture.image_height();
|
2021-12-13 18:19:23 -08:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
// log::debug!("Image dimensions: {} x {}", width, height);
|
|
|
|
|
// Ok((width, height))
|
|
|
|
|
Ok(())
|
2021-03-02 16:39:03 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
// Builds an `LpFileModel`, which is an implementation of `gio::ListModel`
|
|
|
|
|
// that holds a `gio::File` for each child within the same directory as the
|
|
|
|
|
// file we pass. This model will update with changes to the directory,
|
|
|
|
|
// and in turn we'll update our `adw::Carousel`.
|
|
|
|
|
//
|
|
|
|
|
// TODO: Properly loading a new model with the same view,
|
|
|
|
|
// & loading a file from the same directory
|
|
|
|
|
fn build_model_from_file(&self, file: &gio::File) {
|
2021-12-29 14:02:48 -08:00
|
|
|
let imp = self.imp();
|
2022-01-19 23:11:38 -08:00
|
|
|
let carousel = &imp.carousel;
|
2022-01-16 19:03:13 -08:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
{
|
|
|
|
|
// Here we use a nested scope so that the mutable borrow only lasts as long as we need it
|
|
|
|
|
let mut model = imp.model.borrow_mut();
|
|
|
|
|
|
|
|
|
|
if let Some(ref parent) = file.parent() {
|
|
|
|
|
if let Some(ref m) = *model {
|
|
|
|
|
if m.directory().map_or(false, |f| !f.equal(parent)) {
|
|
|
|
|
*model = Some(LpFileModel::from_directory(parent));
|
|
|
|
|
log::debug!("new model created");
|
|
|
|
|
} else {
|
|
|
|
|
// Early return if the parent is equal to the current model's directory
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-01-16 19:03:13 -08:00
|
|
|
*model = Some(LpFileModel::from_directory(parent));
|
|
|
|
|
log::debug!("new model created");
|
|
|
|
|
}
|
2021-09-05 05:51:34 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
if let Some(model) = imp.model.borrow().as_ref() {
|
|
|
|
|
let index = model.index_of(file).unwrap() as u32;
|
|
|
|
|
imp.filename.replace(util::get_file_display_name(file));
|
|
|
|
|
carousel.append(&LpImagePage::from_file(file));
|
2021-09-05 05:51:34 -07:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
// Here we need to check if we're at the start or end of our directory.
|
|
|
|
|
// If we are, we try to add two files to the other side. Otherwise,
|
|
|
|
|
// add one file on each end of the current file.
|
|
|
|
|
let iterations = if index == 0 || index == model.n_items() - 1 {
|
|
|
|
|
2
|
|
|
|
|
} else {
|
|
|
|
|
1
|
|
|
|
|
};
|
2021-09-05 05:51:34 -07:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
let mut next_file = file.clone();
|
|
|
|
|
for i in 0..iterations {
|
|
|
|
|
log::debug!("Loop to find next files: {}", i);
|
|
|
|
|
if let Some(f) = model.next(&next_file) {
|
|
|
|
|
log::debug!("Adding next image URI: {}", f.uri());
|
|
|
|
|
carousel.append(&LpImagePage::from_file(&f));
|
|
|
|
|
next_file = f;
|
|
|
|
|
} else {
|
|
|
|
|
// Return early if there are no files in this direction;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-01-16 19:03:13 -08:00
|
|
|
}
|
2021-09-05 05:51:34 -07:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
let mut prev_file = file.clone();
|
|
|
|
|
for i in 0..iterations {
|
|
|
|
|
log::debug!("Loop to find prior files: {}", i);
|
|
|
|
|
if let Some(f) = model.previous(&prev_file) {
|
|
|
|
|
log::debug!("Adding previous image URI: {}", f.uri());
|
|
|
|
|
carousel.prepend(&LpImagePage::from_file(&f));
|
|
|
|
|
prev_file = f;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2022-01-16 19:03:13 -08:00
|
|
|
}
|
|
|
|
|
}
|
2022-01-19 23:11:38 -08:00
|
|
|
}
|
2022-01-16 19:03:13 -08:00
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
pub fn navigate(&self, direction: adw::NavigationDirection) {
|
|
|
|
|
let carousel = &self.imp().carousel;
|
|
|
|
|
let pos = carousel.position().round() as u32;
|
|
|
|
|
match direction {
|
|
|
|
|
adw::NavigationDirection::Forward => {
|
|
|
|
|
if pos < carousel.n_pages() - 1 {
|
|
|
|
|
carousel.scroll_to(&carousel.nth_page(pos + 1), true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
adw::NavigationDirection::Back => {
|
|
|
|
|
if pos > 0 {
|
|
|
|
|
carousel.scroll_to(&carousel.nth_page(pos - 1), true)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => unimplemented!("Navigation direction should only be back or forward."),
|
|
|
|
|
};
|
2021-09-05 05:51:34 -07:00
|
|
|
}
|
|
|
|
|
|
2022-01-16 19:03:13 -08:00
|
|
|
pub fn update_action_state(&self, file: &gio::File) {
|
2021-12-29 14:02:48 -08:00
|
|
|
let imp = self.imp();
|
2022-01-16 19:03:13 -08:00
|
|
|
let b = imp.model.borrow();
|
|
|
|
|
let model = b.as_ref().unwrap();
|
|
|
|
|
|
|
|
|
|
let next_enabled = model.next(file).is_some();
|
|
|
|
|
let prev_enabled = model.previous(file).is_some();
|
|
|
|
|
|
|
|
|
|
self.action_set_enabled("iv.next", next_enabled);
|
|
|
|
|
self.action_set_enabled("iv.previous", prev_enabled);
|
2021-09-05 05:51:34 -07:00
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
#[template_callback]
|
|
|
|
|
fn page_changed_cb(&self, index: u32, carousel: &adw::Carousel) {
|
|
|
|
|
let imp = self.imp();
|
|
|
|
|
let b = imp.model.borrow();
|
|
|
|
|
let model = b.as_ref().unwrap();
|
|
|
|
|
let current = self.current_page().and_then(|p| p.file()).unwrap();
|
|
|
|
|
imp.filename.replace(util::get_file_display_name(¤t));
|
|
|
|
|
self.notify("filename");
|
|
|
|
|
self.update_action_state(¤t);
|
|
|
|
|
|
|
|
|
|
// We've moved forward
|
|
|
|
|
if index + 1 == N_PAGES {
|
|
|
|
|
if let Some(ref next) = model.next(¤t) {
|
|
|
|
|
log::debug!("Next URI: {}", next.uri());
|
|
|
|
|
// Remove the page at index 0, add a new page at the end
|
|
|
|
|
carousel.remove(&carousel.nth_page(0));
|
|
|
|
|
carousel.append(&LpImagePage::from_file(next));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We've moved backward
|
|
|
|
|
if index == 0 {
|
|
|
|
|
if let Some(ref prev) = model.previous(¤t) {
|
|
|
|
|
log::debug!("Previous URI: {}", prev.uri());
|
|
|
|
|
// Remove the page at the front, add a new page at the back
|
|
|
|
|
carousel.remove(&carousel.nth_page(carousel.n_pages() - 1));
|
|
|
|
|
carousel.prepend(&LpImagePage::from_file(prev));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-26 02:05:34 -08:00
|
|
|
pub fn set_wallpaper(&self) -> anyhow::Result<()> {
|
|
|
|
|
let wallpaper = self.uri().context("No URI for current file")?;
|
2021-08-17 20:37:04 -07:00
|
|
|
let ctx = glib::MainContext::default();
|
2021-12-28 16:38:36 -08:00
|
|
|
ctx.spawn_local(clone!(@weak self as view => async move {
|
2021-12-29 18:40:34 -08:00
|
|
|
let id = WindowIdentifier::from_native(
|
|
|
|
|
&view.native().expect("View should have a GtkNative"),
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
|
2021-12-28 16:38:36 -08:00
|
|
|
let status = match wallpaper::set_from_uri(
|
2021-12-29 18:40:34 -08:00
|
|
|
&id,
|
2021-12-28 16:38:36 -08:00
|
|
|
&wallpaper,
|
|
|
|
|
true,
|
|
|
|
|
wallpaper::SetOn::Background,
|
|
|
|
|
)
|
|
|
|
|
.await {
|
|
|
|
|
Ok(_) => "Set as wallpaper.",
|
|
|
|
|
Err(_) => "Could not set wallpaper.",
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
view.activate_action(
|
|
|
|
|
"win.show-toast",
|
|
|
|
|
// We use `1` here because we can't pass enums directly as GVariants,
|
|
|
|
|
// so we need to use the C int value of the enum.
|
|
|
|
|
// `TOAST_PRIORITY_NORMAL = 0`, and `TOAST_PRIORITY_HIGH = 1`
|
|
|
|
|
Some(&(status, 1).to_variant()),
|
|
|
|
|
)
|
|
|
|
|
.unwrap();
|
|
|
|
|
}));
|
2020-12-26 02:05:34 -08:00
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn print(&self) -> anyhow::Result<()> {
|
|
|
|
|
let operation = gtk::PrintOperation::new();
|
2022-01-19 23:11:38 -08:00
|
|
|
let path = self
|
|
|
|
|
.current_page()
|
|
|
|
|
.and_then(|p| p.file())
|
2022-01-16 19:03:13 -08:00
|
|
|
.context("No file")?
|
|
|
|
|
.peek_path()
|
|
|
|
|
.context("No path")?;
|
2020-12-26 02:05:34 -08:00
|
|
|
let pb = gdk_pixbuf::Pixbuf::from_file(path)?;
|
|
|
|
|
|
|
|
|
|
let setup = gtk::PageSetup::default();
|
|
|
|
|
let page_size = gtk::PaperSize::new(Some(>k::PAPER_NAME_A4));
|
|
|
|
|
setup.set_paper_size(&page_size);
|
|
|
|
|
operation.set_default_page_setup(Some(&setup));
|
|
|
|
|
|
|
|
|
|
let settings = gtk::PrintSettings::default();
|
|
|
|
|
operation.set_print_settings(Some(&settings));
|
|
|
|
|
|
|
|
|
|
operation.connect_begin_print(move |op, _| {
|
|
|
|
|
op.set_n_pages(1);
|
|
|
|
|
});
|
|
|
|
|
|
2021-09-28 22:59:37 -07:00
|
|
|
// FIXME: Rework all of this after reading https://cairographics.org/manual/cairo-Image-Surfaces.html
|
|
|
|
|
// Since I don't know cairo; See also eog-print.c
|
2021-03-02 15:09:44 -08:00
|
|
|
operation.connect_draw_page(clone!(@weak pb => move |_, ctx, _| {
|
2022-01-08 22:18:12 -08:00
|
|
|
let cr = ctx.cairo_context();
|
2020-12-26 02:05:34 -08:00
|
|
|
cr.set_source_pixbuf(&pb, 0.0, 0.0);
|
2021-09-28 22:59:37 -07:00
|
|
|
let _ = cr.paint();
|
2020-12-26 02:05:34 -08:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
log::debug!("Running print operation...");
|
2021-06-27 15:28:35 -07:00
|
|
|
let root = self.root().context("Could not get root for widget")?;
|
2020-12-26 02:05:34 -08:00
|
|
|
let window = root
|
|
|
|
|
.downcast_ref::<gtk::Window>()
|
|
|
|
|
.context("Could not downcast to GtkWindow")?;
|
|
|
|
|
operation.run(gtk::PrintOperationAction::PrintDialog, Some(window))?;
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2022-01-08 14:02:16 +01:00
|
|
|
pub fn copy(&self) -> anyhow::Result<()> {
|
|
|
|
|
let clipboard = self.clipboard();
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
if let Some(texture) = self.current_page().context("No current page")?.texture() {
|
2022-01-08 14:02:16 +01:00
|
|
|
clipboard.set_texture(&texture);
|
|
|
|
|
} else {
|
|
|
|
|
anyhow::bail!("No Image displayed.");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-05 13:26:33 -08:00
|
|
|
pub fn current_page(&self) -> Option<LpImagePage> {
|
2022-01-19 23:11:38 -08:00
|
|
|
let carousel = &self.imp().carousel;
|
|
|
|
|
let pos = carousel.position().round() as u32;
|
|
|
|
|
if carousel.n_pages() > 0 {
|
|
|
|
|
carousel.nth_page(pos).downcast().ok()
|
|
|
|
|
} else {
|
|
|
|
|
None
|
|
|
|
|
}
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
pub fn uri(&self) -> Option<String> {
|
|
|
|
|
let page = self.current_page().expect("No page");
|
|
|
|
|
let file = page.file().expect("No file");
|
|
|
|
|
Some(file.uri().to_string())
|
2021-09-05 05:51:34 -07:00
|
|
|
}
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
pub fn filename(&self) -> Option<String> {
|
|
|
|
|
self.imp().filename.borrow().clone()
|
2020-12-26 02:05:34 -08:00
|
|
|
}
|
|
|
|
|
}
|