diff --git a/po/POTFILES.in b/po/POTFILES.in
index 9127299..d2b2899 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -22,6 +22,8 @@ src/widgets.rs
src/widgets/drag_overlay.rs
src/widgets/error_details.rs
src/widgets/error_details.ui
+src/widgets/fullscreen_widget.rs
+src/widgets/fullscreen_widget.ui
src/widgets/image.rs
src/widgets/image/background_color.rs
src/widgets/image/drag.rs
diff --git a/src/widgets.rs b/src/widgets.rs
index affcffd..132d856 100644
--- a/src/widgets.rs
+++ b/src/widgets.rs
@@ -19,6 +19,7 @@
mod drag_overlay;
mod error_details;
+mod fullscreen_widget;
mod image;
mod image_page;
mod image_view;
@@ -30,6 +31,7 @@ mod sliding_view;
pub use drag_overlay::LpDragOverlay;
pub use error_details::LpErrorDetails;
+pub use fullscreen_widget::LpFullscreenWidget;
pub use image::LpImage;
pub use image_page::LpImagePage;
pub use image_view::LpImageView;
diff --git a/src/widgets/fullscreen_widget.rs b/src/widgets/fullscreen_widget.rs
new file mode 100644
index 0000000..6c699ef
--- /dev/null
+++ b/src/widgets/fullscreen_widget.rs
@@ -0,0 +1,147 @@
+// Copyright (c) 2024 Christopher Davis
+//
+// 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 .
+//
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+use std::cell::{Cell, RefCell};
+
+use adw::prelude::*;
+use adw::subclass::prelude::*;
+use glib::Properties;
+use gtk::CompositeTemplate;
+
+use super::*;
+use crate::deps::*;
+
+mod imp {
+ use super::*;
+
+ #[derive(Default, Debug, Properties, CompositeTemplate)]
+ #[properties(wrapper_type = super::LpFullscreenWidget)]
+ #[template(file = "fullscreen_widget.ui")]
+ pub struct LpFullscreenWidget {
+ #[template_child]
+ pub(super) toolbar_view: TemplateChild,
+ #[template_child]
+ pub(super) header_container: TemplateChild,
+
+ #[property(get, set, nullable)]
+ headerbar: RefCell>,
+ #[property(get, set, nullable)]
+ content: RefCell >,
+
+ #[property(get, set)]
+ fullscreened: Cell,
+ #[property(get, set)]
+ extend_content: Cell,
+ #[property(get = LpFullscreenWidget::is_content_extended_to_top, type = bool)]
+ _is_content_extended_to_top: (),
+ #[property(get, set)]
+ is_empty: Cell,
+ #[property(get, set)]
+ headerbar_opacity: Cell,
+ }
+
+ impl LpFullscreenWidget {
+ fn is_content_extended_to_top(&self) -> bool {
+ let obj = self.obj();
+ obj.fullscreened() && obj.extend_content()
+ }
+ }
+
+ #[glib::object_subclass]
+ impl ObjectSubclass for LpFullscreenWidget {
+ const NAME: &'static str = "LpFullscreenWidget";
+ type Type = super::LpFullscreenWidget;
+ type ParentType = adw::Bin;
+
+ fn class_init(klass: &mut Self::Class) {
+ klass.bind_template();
+ }
+
+ fn instance_init(obj: &gio::glib::subclass::InitializingObject) {
+ obj.init_template();
+ }
+ }
+
+ #[glib::derived_properties]
+ impl ObjectImpl for LpFullscreenWidget {
+ fn constructed(&self) {
+ self.parent_constructed();
+
+ let obj = self.obj();
+ obj.connect_is_empty_notify(|obj| {
+ obj.update_headerbar_style();
+ });
+
+ obj.connect_fullscreened_notify(|obj| {
+ obj.update_headerbar_style();
+ });
+
+ obj.connect_extend_content_notify(|obj| {
+ obj.update_headerbar_style();
+ });
+ }
+ }
+
+ impl WidgetImpl for LpFullscreenWidget {}
+ impl BinImpl for LpFullscreenWidget {}
+}
+
+glib::wrapper! {
+ pub struct LpFullscreenWidget(ObjectSubclass)
+ @extends gtk::Widget, adw::Bin,
+ @implements gtk::Buildable, gtk::ConstraintTarget;
+}
+
+impl LpFullscreenWidget {
+ pub fn is_headerbar_flat(&self) -> bool {
+ matches!(
+ self.imp().toolbar_view.top_bar_style(),
+ adw::ToolbarStyle::Flat
+ )
+ }
+
+ fn update_header_opacity(&self) {
+ if let Some(headerbar) = self.headerbar() {
+ if self.is_headerbar_flat() && !self.is_empty() {
+ // Bring headerbar opacity in sync with controls
+ headerbar.set_opacity(self.headerbar_opacity());
+ } else {
+ headerbar.set_opacity(1.);
+ };
+ }
+ }
+
+ fn update_headerbar_style(&self) {
+ self.imp()
+ .toolbar_view
+ .set_extend_content_to_top_edge(self.is_content_extended_to_top());
+
+ // Flat headerbar for empty state and fullscreen
+ let style = if self.is_empty() || self.is_content_extended_to_top() {
+ adw::ToolbarStyle::Flat
+ } else {
+ // Use the border variant of raised to avoid shadows over images
+ adw::ToolbarStyle::RaisedBorder
+ };
+
+ let toolbar_view = self.imp().toolbar_view.get();
+ if style != toolbar_view.top_bar_style() {
+ toolbar_view.set_top_bar_style(style);
+ self.update_header_opacity();
+ }
+ }
+}
diff --git a/src/widgets/fullscreen_widget.ui b/src/widgets/fullscreen_widget.ui
new file mode 100644
index 0000000..7ace3a7
--- /dev/null
+++ b/src/widgets/fullscreen_widget.ui
@@ -0,0 +1,30 @@
+
+
+
+
+
+ LpFullscreenWidget
+
+
+
+
+ LpFullscreenWidget
+
+
+
+
+ LpFullscreenWidget
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/window.rs b/src/window.rs
index 37994c0..47dfe05 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -34,7 +34,7 @@ use std::path::{Path, PathBuf};
use actions::*;
use adw::prelude::*;
use adw::subclass::prelude::*;
-use glib::clone;
+use glib::{clone, Properties};
use gtk::{CompositeTemplate, Widget};
use crate::application::LpApplication;
@@ -42,7 +42,7 @@ use crate::config;
use crate::deps::*;
use crate::util::gettext::*;
use crate::util::Direction;
-use crate::widgets::{LpDragOverlay, LpImage, LpImageView, LpPropertiesView};
+use crate::widgets::{LpDragOverlay, LpFullscreenWidget, LpImage, LpImageView, LpPropertiesView};
/// Show window after X milliseconds even if image dimensions are not known yet
const SHOW_WINDOW_AFTER: u64 = 2000;
@@ -73,14 +73,15 @@ mod imp {
// If some member fields did not implement default,
// we'd need to have a `new()` function in the
// `impl ObjectSubclass for $TYPE` section.
- #[derive(Default, Debug, CompositeTemplate)]
+ #[derive(Default, Debug, Properties, CompositeTemplate)]
+ #[properties(wrapper_type = super::LpWindow)]
#[template(file = "window.ui")]
pub struct LpWindow {
// Template children are used with the
// TemplateChild wrapper, where T is the
// object type of the template child.
#[template_child]
- pub(super) toolbar_view: TemplateChild,
+ pub(super) fullscreen_widget: TemplateChild,
#[template_child]
pub(super) headerbar: TemplateChild,
#[template_child]
@@ -110,6 +111,11 @@ mod imp {
#[template_child]
pub(super) backward_click_gesture: TemplateChild,
+ #[property(get, set)]
+ is_empty: Cell,
+ #[property(get, set)]
+ headerbar_opacity: Cell,
+
/// Motion controller for complete window
pub(super) motion_controller: gtk::EventControllerMotion,
pub(super) pointer_position: Cell<(f64, f64)>,
@@ -167,6 +173,7 @@ mod imp {
}
}
+ #[glib::derived_properties]
impl ObjectImpl for LpWindow {
fn constructed(&self) {
self.parent_constructed();
@@ -233,12 +240,6 @@ mod imp {
win.resize_default();
});
- self.properties_button.connect_active_notify(glib::clone!(
- #[weak]
- obj,
- move |_| obj.update_headerbar_style()
- ));
-
let gesture_click = gtk::GestureClick::new();
gesture_click.connect_pressed(glib::clone!(
#[weak]
@@ -339,11 +340,11 @@ mod imp {
),
);
- self.toolbar_view.connect_top_bar_style_notify(glib::clone!(
- #[weak]
- obj,
- move |_| obj.on_top_bar_style_notify()
- ));
+ self.image_view
+ .controls_box_start()
+ .bind_property("opacity", &*self.obj(), "headerbar-opacity")
+ .sync_create()
+ .build();
// action win.previous status
self.image_view
@@ -760,7 +761,7 @@ impl LpWindow {
let current_page = imp.image_view.current_page();
// HeaderBar style
- self.update_headerbar_style();
+ self.set_is_empty(current_page.is_none());
// Window title
self.update_title();
@@ -896,8 +897,6 @@ impl LpWindow {
.image_view
.on_fullscreen_changed(self.is_fullscreen());
- self.update_headerbar_style();
-
if !self.is_fullscreen() {
self.set_cursor(None);
self.show_controls();
@@ -906,39 +905,11 @@ impl LpWindow {
}
fn is_headerbar_flat(&self) -> bool {
- matches!(
- self.imp().toolbar_view.top_bar_style(),
- adw::ToolbarStyle::Flat
- )
- }
-
- fn on_top_bar_style_notify(&self) {
- if self.is_headerbar_flat() {
- // Bring headerbar opacity in sync with controls
- self.headerbar().set_opacity(self.controls_opacity());
- } else {
- self.headerbar().set_opacity(1.);
- }
+ self.imp().fullscreen_widget.is_headerbar_flat()
}
fn is_content_extended_to_top(&self) -> bool {
- self.is_fullscreen() && !self.imp().properties_button.is_active()
- }
-
- fn update_headerbar_style(&self) {
- let imp = self.imp();
-
- let extend = self.is_content_extended_to_top();
- imp.toolbar_view.set_extend_content_to_top_edge(extend);
-
- // Flat headerbar for empty state and fullscreen
- let toolbar_style = if imp.image_view.current_image().is_none() || extend {
- adw::ToolbarStyle::Flat
- } else {
- // Use the border variant of raised to avoid shadows over images
- adw::ToolbarStyle::RaisedBorder
- };
- imp.toolbar_view.set_top_bar_style(toolbar_style);
+ self.imp().fullscreen_widget.is_content_extended_to_top()
}
fn update_accel_status(&self) {
diff --git a/src/window.ui b/src/window.ui
index 4231d67..e241578 100644
--- a/src/window.ui
+++ b/src/window.ui
@@ -68,75 +68,72 @@
-
-
-
-
-
+
+
+ user-trash-symbolic
+ win.trash
+ Move to Trash
+
+
+
+
+
+
+
+ info-outline-symbolic
+ Image Properties
+
+
+
+
+
-
+