2022-01-19 23:11:38 -08:00
|
|
|
// image_page.rs
|
|
|
|
|
//
|
|
|
|
|
// Copyright 2022 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
|
|
|
|
|
|
2023-03-06 22:59:40 +01:00
|
|
|
//! A widget that shows the image, a loading screen, or an error page
|
|
|
|
|
//!
|
|
|
|
|
//! This widget also handles showing the context menu.
|
|
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
use crate::deps::*;
|
2023-01-23 01:10:15 +01:00
|
|
|
use crate::widgets::LpImage;
|
2022-01-19 23:11:38 -08:00
|
|
|
|
2023-04-06 21:14:27 +02:00
|
|
|
use crate::util::spawn;
|
2022-01-19 23:11:38 -08:00
|
|
|
use adw::subclass::prelude::*;
|
|
|
|
|
use glib::clone;
|
|
|
|
|
use gtk::prelude::*;
|
|
|
|
|
use gtk::CompositeTemplate;
|
2023-01-23 01:10:15 +01:00
|
|
|
use once_cell::sync::Lazy;
|
2022-08-03 19:32:02 -04:00
|
|
|
|
2023-01-23 01:10:15 +01:00
|
|
|
use std::path::{Path, PathBuf};
|
2022-01-19 23:11:38 -08:00
|
|
|
|
|
|
|
|
mod imp {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, CompositeTemplate)]
|
2023-01-23 01:10:15 +01:00
|
|
|
#[template(file = "../../data/gtk/image_page.ui")]
|
2022-01-19 23:11:38 -08:00
|
|
|
pub struct LpImagePage {
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub(super) stack: TemplateChild<gtk::Stack>,
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub(super) spinner: TemplateChild<gtk::Spinner>,
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub(super) error_page: TemplateChild<adw::StatusPage>,
|
|
|
|
|
#[template_child]
|
2023-03-18 01:22:30 +01:00
|
|
|
pub(super) image_stack_page: TemplateChild<gtk::Widget>,
|
2022-11-10 19:39:23 +01:00
|
|
|
#[template_child]
|
2023-03-21 21:50:54 +01:00
|
|
|
pub(super) scrolled_window: TemplateChild<gtk::ScrolledWindow>,
|
|
|
|
|
#[template_child]
|
2022-01-19 23:11:38 -08:00
|
|
|
pub(super) image: TemplateChild<LpImage>,
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub(super) popover: TemplateChild<gtk::PopoverMenu>,
|
|
|
|
|
#[template_child]
|
2023-02-06 18:40:32 +01:00
|
|
|
pub(super) right_click_gesture: TemplateChild<gtk::GestureClick>,
|
2022-01-19 23:11:38 -08:00
|
|
|
#[template_child]
|
|
|
|
|
pub(super) press_gesture: TemplateChild<gtk::GestureLongPress>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[glib::object_subclass]
|
|
|
|
|
impl ObjectSubclass for LpImagePage {
|
|
|
|
|
const NAME: &'static str = "LpImagePage";
|
|
|
|
|
type Type = super::LpImagePage;
|
|
|
|
|
type ParentType = adw::Bin;
|
|
|
|
|
|
|
|
|
|
fn class_init(klass: &mut Self::Class) {
|
|
|
|
|
Self::bind_template(klass);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
|
|
|
|
obj.init_template();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ObjectImpl for LpImagePage {
|
2022-11-10 19:39:23 +01:00
|
|
|
fn properties() -> &'static [glib::ParamSpec] {
|
|
|
|
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
vec![glib::ParamSpecObject::builder::<LpImage>("image")
|
|
|
|
|
.read_only()
|
|
|
|
|
.build()]
|
2022-11-10 19:39:23 +01:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
PROPERTIES.as_ref()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
2023-02-12 02:07:42 +01:00
|
|
|
let obj = self.obj();
|
2022-11-10 19:39:23 +01:00
|
|
|
match pspec.name() {
|
|
|
|
|
"image" => obj.image().to_value(),
|
|
|
|
|
name => unimplemented!("property {name}"),
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-23 01:10:15 +01:00
|
|
|
|
2022-11-02 21:11:43 +01:00
|
|
|
fn constructed(&self) {
|
2023-02-12 02:07:42 +01:00
|
|
|
let obj = self.obj();
|
2022-11-02 21:11:43 +01:00
|
|
|
|
|
|
|
|
self.parent_constructed();
|
2022-01-19 23:11:38 -08:00
|
|
|
|
2023-02-06 18:40:32 +01:00
|
|
|
self.right_click_gesture
|
2022-02-05 16:26:15 -08:00
|
|
|
.connect_pressed(clone!(@weak obj => move |gesture, _, x, y| {
|
2022-01-19 23:11:38 -08:00
|
|
|
obj.show_popover_at(x, y);
|
2022-02-05 16:26:15 -08:00
|
|
|
gesture.set_state(gtk::EventSequenceState::Claimed);
|
2022-01-19 23:11:38 -08:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
self.press_gesture
|
2022-02-05 16:26:15 -08:00
|
|
|
.connect_pressed(clone!(@weak obj => move |gesture, x, y| {
|
|
|
|
|
log::debug!("Long press triggered");
|
2022-01-19 23:11:38 -08:00
|
|
|
obj.show_popover_at(x, y);
|
2022-02-05 16:26:15 -08:00
|
|
|
gesture.set_state(gtk::EventSequenceState::Claimed);
|
2022-01-19 23:11:38 -08:00
|
|
|
}));
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
|
|
|
|
|
obj.image().connect_notify_local(
|
|
|
|
|
Some("is-loaded"),
|
|
|
|
|
clone!(@weak obj => move |_,_| {
|
|
|
|
|
if obj.image().is_loaded() {
|
|
|
|
|
obj.imp()
|
|
|
|
|
.stack
|
2023-03-18 01:22:30 +01:00
|
|
|
.set_visible_child(&*obj.imp().image_stack_page);
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
obj.image().connect_notify_local(
|
|
|
|
|
Some("error"),
|
|
|
|
|
clone!(@weak obj => move |_,_| {
|
|
|
|
|
if obj.image().error().is_some() {
|
|
|
|
|
obj.imp().stack.set_visible_child(&*obj.imp().error_page);
|
|
|
|
|
}
|
|
|
|
|
}),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Do not waste CPU on spinner if it is not visible
|
|
|
|
|
self.spinner.connect_map(|s| s.start());
|
|
|
|
|
self.spinner.connect_unmap(|s| s.stop());
|
2022-01-19 23:11:38 -08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl WidgetImpl for LpImagePage {}
|
|
|
|
|
impl BinImpl for LpImagePage {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
glib::wrapper! {
|
|
|
|
|
pub struct LpImagePage(ObjectSubclass<imp::LpImagePage>)
|
|
|
|
|
@extends gtk::Widget, adw::Bin,
|
|
|
|
|
@implements gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl LpImagePage {
|
2023-01-23 01:10:15 +01:00
|
|
|
pub fn from_path(path: &Path) -> Self {
|
2023-02-12 02:07:42 +01:00
|
|
|
let obj = glib::Object::new::<Self>();
|
2023-01-23 01:10:15 +01:00
|
|
|
let path = path.to_path_buf();
|
|
|
|
|
let file = gio::File::for_path(&path);
|
|
|
|
|
|
|
|
|
|
obj.imp().image.set_file(&file);
|
2022-01-19 23:11:38 -08:00
|
|
|
|
2023-04-06 21:14:27 +02:00
|
|
|
spawn(clone!(@weak obj, @strong path => async move {
|
2022-01-19 23:11:38 -08:00
|
|
|
let imp = obj.imp();
|
decoder: Use image-rs, libheif-rs, librsvg
This adds proper support for a lot of image formats including SVGs.
It will be the basis for many features to come. The technical core
is an asynchronous renderer that can arrange textures in smaller tiles
which might be generated on demand.
Closes #34, #40, #41, #49
Basis for fixing the following issues #7, #27, #36, #43, #68, #71
2023-03-04 03:14:52 +01:00
|
|
|
imp.image.load(&path).await;
|
2022-01-19 23:11:38 -08:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
obj
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-23 01:10:15 +01:00
|
|
|
pub fn path(&self) -> PathBuf {
|
|
|
|
|
self.image().path().unwrap()
|
2022-01-19 23:11:38 -08:00
|
|
|
}
|
|
|
|
|
|
2023-03-21 21:50:54 +01:00
|
|
|
pub fn scrolled_window(&self) -> gtk::ScrolledWindow {
|
|
|
|
|
self.imp().scrolled_window.clone()
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 17:40:06 +01:00
|
|
|
pub fn image(&self) -> LpImage {
|
|
|
|
|
self.imp().image.get()
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-28 18:27:40 +01:00
|
|
|
pub fn content_provider(&self) -> Option<gdk::ContentProvider> {
|
2022-01-19 23:11:38 -08:00
|
|
|
self.imp().image.content_provider()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn show_popover_at(&self, x: f64, y: f64) {
|
|
|
|
|
let imp = self.imp();
|
|
|
|
|
|
|
|
|
|
let rect = gdk::Rectangle::new(x as i32, y as i32, 0, 0);
|
|
|
|
|
|
|
|
|
|
imp.popover.set_pointing_to(Some(&rect));
|
|
|
|
|
imp.popover.popup();
|
|
|
|
|
}
|
|
|
|
|
}
|