2023-04-19 20:02:01 +02:00
|
|
|
// Copyright (c) 2022-2023 Sophie Herold
|
|
|
|
|
// Copyright (c) 2022 Christopher Davis
|
2022-01-19 23:11:38 -08:00
|
|
|
//
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
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
|
|
|
|
2022-01-19 23:11:38 -08:00
|
|
|
mod imp {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Default, CompositeTemplate)]
|
2023-11-28 21:50:51 +01:00
|
|
|
#[template(file = "image_page.ui")]
|
2022-01-19 23:11:38 -08:00
|
|
|
pub struct LpImagePage {
|
|
|
|
|
#[template_child]
|
|
|
|
|
pub(super) stack: TemplateChild<gtk::Stack>,
|
|
|
|
|
#[template_child]
|
2023-04-26 18:15:36 +02:00
|
|
|
pub(super) spinner_page: TemplateChild<gtk::Widget>,
|
2022-01-19 23:11:38 -08:00
|
|
|
#[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-04-26 18:15:36 +02:00
|
|
|
pub(super) spinner: TemplateChild<gtk::Spinner>,
|
|
|
|
|
#[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
|
|
|
|
2023-04-26 18:15:36 +02:00
|
|
|
self.stack
|
|
|
|
|
.connect_visible_child_notify(clone!(@weak obj => move |stack| {
|
|
|
|
|
let imp = obj.imp();
|
|
|
|
|
let spinner_visible =
|
|
|
|
|
stack.visible_child().as_ref() == Some(imp.spinner_page.upcast_ref());
|
|
|
|
|
// Hide spinner for crossfading into other content since it does not look great
|
|
|
|
|
imp.spinner.set_visible(spinner_visible);
|
|
|
|
|
}));
|
|
|
|
|
|
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() {
|
2023-04-26 18:15:36 +02:00
|
|
|
log::debug!("Showing image");
|
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.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-04-12 01:11:59 +02:00
|
|
|
pub fn from_file(file: &gio::File) -> Self {
|
2023-02-12 02:07:42 +01:00
|
|
|
let obj = glib::Object::new::<Self>();
|
2023-01-23 01:10:15 +01:00
|
|
|
|
2023-04-12 04:13:30 +02:00
|
|
|
obj.imp().image.set_file(file);
|
2022-01-19 23:11:38 -08:00
|
|
|
|
2023-11-22 09:12:07 +01:00
|
|
|
glib::spawn_future_local(clone!(@weak obj, @strong file => async move {
|
2022-01-19 23:11:38 -08:00
|
|
|
let imp = obj.imp();
|
2023-04-12 01:11:59 +02:00
|
|
|
imp.image.load(&file).await;
|
2022-01-19 23:11:38 -08:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
obj
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-12 01:11:59 +02:00
|
|
|
pub fn file(&self) -> gio::File {
|
|
|
|
|
self.image().file().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();
|
|
|
|
|
}
|
|
|
|
|
}
|