ui: Non-circular overlays and less buttons
Part-of: <https://gitlab.gnome.org/GNOME/loupe/-/merge_requests/450>
This commit is contained in:
parent
e5930b134c
commit
0b4c69459f
18 changed files with 470 additions and 93 deletions
|
|
@ -184,6 +184,7 @@ mod imp {
|
|||
pub(super) zoom_animation: OnceCell<adw::TimedAnimation>,
|
||||
/// Targeted zoom level, might differ from `zoom` when animation is
|
||||
/// running
|
||||
#[property(get, set = Self::set_zoom_target)]
|
||||
pub(super) zoom_target: Cell<f64>,
|
||||
/// Point in image that should stay under the cursor during animation.
|
||||
/// The value is in image coordinates.
|
||||
|
|
|
|||
|
|
@ -212,6 +212,7 @@ impl imp::LpImage {
|
|||
log::trace!("Setting zoom target {zoom_target}");
|
||||
|
||||
self.zoom_target.set(zoom_target);
|
||||
self.obj().notify_zoom_target();
|
||||
|
||||
if self.obj().zoom() == self.zoom_target.get() {
|
||||
self.request_tiles();
|
||||
|
|
@ -365,11 +366,16 @@ impl LpImage {
|
|||
|
||||
/// Zoom to specific level with animation not snapping to best-fit
|
||||
///
|
||||
/// Used for zooming to 100% or 200%
|
||||
/// Used for zooming to 100% or 200% etc
|
||||
pub fn zoom_to_exact(&self, zoom: f64) {
|
||||
self.imp().zoom_to_full(zoom, true, false, false);
|
||||
}
|
||||
|
||||
/// Same as `zoom_to_exact` but zoom to center
|
||||
pub fn zoom_to_exact_center(&self, zoom: f64) {
|
||||
self.imp().zoom_to_full(zoom, true, false, true);
|
||||
}
|
||||
|
||||
pub fn is_best_fit(&self) -> bool {
|
||||
self.imp().best_fit.get()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,18 +6,10 @@
|
|||
<attribute name="label" translatable="yes">_Open With…</attribute>
|
||||
<attribute name="action">win.open-with</attribute>
|
||||
</item>
|
||||
<!--<item>
|
||||
<attribute name="label" translatable="yes">_Display On…</attribute>
|
||||
<attribute name="action">win.display-on</attribute>
|
||||
</item>-->
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Print…</attribute>
|
||||
<attribute name="action">win.print</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Copy</attribute>
|
||||
<attribute name="action">win.copy-image</attribute>
|
||||
</item>
|
||||
</section>
|
||||
<section>
|
||||
<attribute name="label" translatable="yes">Rotate</attribute>
|
||||
|
|
@ -34,6 +26,14 @@
|
|||
<attribute name="label" translatable="yes">_Set as Background…</attribute>
|
||||
<attribute name="action">win.set-background</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Delete</attribute>
|
||||
<attribute name="action">win.trash</attribute>
|
||||
</item>
|
||||
<item>
|
||||
<attribute name="label" translatable="yes">_Copy</attribute>
|
||||
<attribute name="action">win.copy-image</attribute>
|
||||
</item>
|
||||
</section>
|
||||
</menu>
|
||||
<template class="LpImagePage" parent="AdwBin">
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ use crate::decoder::DecoderError;
|
|||
use crate::deps::*;
|
||||
use crate::file_model::LpFileModel;
|
||||
use crate::util::gettext::*;
|
||||
use crate::util::{Direction, Position};
|
||||
use crate::util::{self, Direction, Position};
|
||||
use crate::widgets::{LpImage, LpImagePage, LpPrint, LpSlidingView};
|
||||
|
||||
// The number of pages we want to buffer
|
||||
|
|
@ -63,18 +63,38 @@ mod imp {
|
|||
/// Direct child of this Adw::Bin
|
||||
#[template_child]
|
||||
pub(super) bin_child: TemplateChild<gtk::Widget>,
|
||||
|
||||
/// overlayed controls
|
||||
#[template_child]
|
||||
pub(super) controls_box_start: TemplateChild<gtk::Widget>,
|
||||
#[template_child]
|
||||
pub(super) zoom_toggle: TemplateChild<gtk::ToggleButton>,
|
||||
#[template_child]
|
||||
pub(super) controls_box_start_events: TemplateChild<gtk::EventControllerMotion>,
|
||||
|
||||
/// overlayed controls
|
||||
#[template_child]
|
||||
pub(super) controls_box_end: TemplateChild<gtk::Widget>,
|
||||
#[template_child]
|
||||
pub(super) controls_box_end_events: TemplateChild<gtk::EventControllerMotion>,
|
||||
#[template_child]
|
||||
pub(super) fullscreen_button: TemplateChild<gtk::Button>,
|
||||
pub(super) zoom_menu_button: TemplateChild<gtk::MenuButton>,
|
||||
#[template_child]
|
||||
pub(super) zoom_to_list: TemplateChild<gtk::ListBox>,
|
||||
#[template_child]
|
||||
pub(super) zoom_to_300: TemplateChild<gtk::ListBoxRow>,
|
||||
#[template_child]
|
||||
pub(super) zoom_to_200: TemplateChild<gtk::ListBoxRow>,
|
||||
#[template_child]
|
||||
pub(super) zoom_to_100: TemplateChild<gtk::ListBoxRow>,
|
||||
#[template_child]
|
||||
pub(super) zoom_to_66: TemplateChild<gtk::ListBoxRow>,
|
||||
#[template_child]
|
||||
pub(super) zoom_to_50: TemplateChild<gtk::ListBoxRow>,
|
||||
#[template_child]
|
||||
pub(super) zoom_value: TemplateChild<gtk::Entry>,
|
||||
#[template_child]
|
||||
pub(super) submit_zoom: TemplateChild<gtk::Button>,
|
||||
|
||||
#[template_child]
|
||||
pub sliding_view: TemplateChild<LpSlidingView>,
|
||||
|
|
@ -98,6 +118,9 @@ mod imp {
|
|||
|
||||
#[property(get = Self::is_next_available)]
|
||||
_is_next_available: PhantomData<bool>,
|
||||
|
||||
#[property(get, set)]
|
||||
zoom_toggle_state: Cell<bool>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
|
|
@ -148,10 +171,10 @@ mod imp {
|
|||
move || obj.target_page_reached()
|
||||
));
|
||||
|
||||
let signal_group = obj.current_image_signals();
|
||||
let current_image_signal_group = obj.current_image_signals();
|
||||
|
||||
let view = &*obj;
|
||||
signal_group.connect_closure(
|
||||
current_image_signal_group.connect_closure(
|
||||
"notify::file",
|
||||
true,
|
||||
glib::closure_local!(
|
||||
|
|
@ -163,7 +186,7 @@ mod imp {
|
|||
),
|
||||
);
|
||||
|
||||
signal_group.connect_closure(
|
||||
current_image_signal_group.connect_closure(
|
||||
"notify::is-deleted",
|
||||
true,
|
||||
glib::closure_local!(
|
||||
|
|
@ -175,6 +198,97 @@ mod imp {
|
|||
),
|
||||
);
|
||||
|
||||
current_image_signal_group.connect_closure(
|
||||
"notify::zoom-target",
|
||||
true,
|
||||
glib::closure_local!(
|
||||
#[watch]
|
||||
view,
|
||||
move |img: LpImage, _: glib::ParamSpec| {
|
||||
let mut percent =
|
||||
((img.zoom_target() * 10_000.).round() / 100.).to_string();
|
||||
|
||||
if let Some(decimal) = util::locale_settings().decimal_point {
|
||||
percent = percent.replace('.', &decimal);
|
||||
}
|
||||
|
||||
view.imp()
|
||||
.zoom_value
|
||||
.set_text(&gettext_f("{} %", [percent]));
|
||||
view.imp().zoom_value.select_region(0, -1);
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
self.zoom_toggle.connect_clicked(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |button| {
|
||||
if let Some(page) = obj.current_page() {
|
||||
if button.is_active() {
|
||||
page.image().zoom_in_center()
|
||||
} else {
|
||||
page.image().zoom_best_fit()
|
||||
};
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.zoom_to_list.connect_row_activated(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |_, row| {
|
||||
let imp = obj.imp();
|
||||
let value = if row == &*imp.zoom_to_300 {
|
||||
3.
|
||||
} else if row == &*imp.zoom_to_200 {
|
||||
2.
|
||||
} else if row == &*imp.zoom_to_100 {
|
||||
1.
|
||||
} else if row == &*imp.zoom_to_66 {
|
||||
0.66
|
||||
} else if row == &*imp.zoom_to_50 {
|
||||
0.5
|
||||
} else {
|
||||
log::error!("Activated unknown entry in zoom to popover.");
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(image) = obj.current_image() {
|
||||
image.zoom_to_exact_center(value);
|
||||
}
|
||||
|
||||
// Close popover after click
|
||||
imp.zoom_menu_button.set_active(false);
|
||||
}
|
||||
));
|
||||
|
||||
self.submit_zoom.connect_clicked(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
move |_| {
|
||||
if let Some(page) = obj.current_page() {
|
||||
let mut value = obj.imp().zoom_value.text().to_string();
|
||||
let locale_settings = util::locale_settings();
|
||||
|
||||
if let Some(thousands) = locale_settings.thousands_sep {
|
||||
value = value.replace(&thousands, "");
|
||||
}
|
||||
|
||||
if let Some(decimal) = locale_settings.decimal_point {
|
||||
value = value.replace(&decimal, ".");
|
||||
}
|
||||
|
||||
value = value.replace('%', "");
|
||||
value = value.trim().to_string();
|
||||
|
||||
if let Ok(value) = value.parse::<f64>() {
|
||||
page.image().zoom_to_exact(value / 100.);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
self.drag_source.set_exclusive(true);
|
||||
|
||||
self.drag_source.connect_prepare(glib::clone!(
|
||||
|
|
@ -678,16 +792,6 @@ impl LpImageView {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn on_fullscreen_changed(&self, is_fullscreened: bool) {
|
||||
let icon = if is_fullscreened {
|
||||
"view-restore-symbolic"
|
||||
} else {
|
||||
"view-fullscreen-symbolic"
|
||||
};
|
||||
|
||||
self.imp().fullscreen_button.set_icon_name(icon);
|
||||
}
|
||||
|
||||
pub fn rotate_image(&self, angle: f64) {
|
||||
if let Some(current_page) = self.current_page() {
|
||||
current_page.image().rotate_by(angle);
|
||||
|
|
@ -770,4 +874,8 @@ impl LpImageView {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn zoom_menu_button(&self) -> gtk::MenuButton {
|
||||
self.imp().zoom_menu_button.clone()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,10 @@
|
|||
<property name="margin-end">18</property>
|
||||
<property name="margin-top">18</property>
|
||||
<property name="margin-bottom">18</property>
|
||||
<property name="spacing">12</property>
|
||||
<property name="spacing">6</property>
|
||||
<style>
|
||||
<class name="osd-box" />
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkButton" id="prev_button">
|
||||
<property name="halign">start</property>
|
||||
|
|
@ -26,8 +29,7 @@
|
|||
<property name="action-name">win.previous</property>
|
||||
<property name="tooltip-text" translatable="yes">Previous Image</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
<class name="circular" />
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
|
@ -37,8 +39,7 @@
|
|||
<property name="action-name">win.next</property>
|
||||
<property name="tooltip-text" translatable="yes">Next Image</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
<class name="circular" />
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
|
|
@ -55,38 +56,203 @@
|
|||
<property name="margin-end">18</property>
|
||||
<property name="margin-top">18</property>
|
||||
<property name="margin-bottom">18</property>
|
||||
<property name="spacing">12</property>
|
||||
<style>
|
||||
<class name="osd-box" />
|
||||
</style>
|
||||
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_out">
|
||||
<property name="icon-name">zoom-out-symbolic</property>
|
||||
<property name="action-name">win.zoom-out-center</property>
|
||||
<property name="tooltip-text" translatable="yes">Zoom Out</property>
|
||||
<object class="GtkRevealer">
|
||||
<property name="reveal-child" bind-source="LpImageView" bind-property="zoom-toggle-state" bind-flags="sync-create" />
|
||||
<property name="transition-type">slide-left</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_out">
|
||||
<property name="icon-name">minus-symbolic</property>
|
||||
<property name="action-name">win.zoom-out-center</property>
|
||||
<property name="tooltip-text" translatable="yes">Zoom Out</property>
|
||||
<style>
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_in">
|
||||
<property name="margin-end">6</property>
|
||||
<property name="icon-name">plus-symbolic</property>
|
||||
<property name="action-name">win.zoom-in-center</property>
|
||||
<property name="tooltip-text" translatable="yes">Zoom In</property>
|
||||
<style>
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton" id="zoom_toggle">
|
||||
<property name="margin-end">6</property>
|
||||
<property name="icon-name">zoom-toggle-symbolic</property>
|
||||
<property name="active" bind-source="LpImageView" bind-property="zoom-toggle-state" bind-flags="sync-create" />
|
||||
<property name="tooltip-text" translatable="yes">Toggle Zoom</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
<class name="circular" />
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="zoom_in">
|
||||
<property name="icon-name">zoom-in-symbolic</property>
|
||||
<property name="action-name">win.zoom-in-center</property>
|
||||
<property name="tooltip-text" translatable="yes">Zoom In</property>
|
||||
<object class="GtkMenuButton" id="zoom_menu_button">
|
||||
<property name="tooltip-text" translatable="yes">Zoom to Specific Level</property>
|
||||
<property name="direction">up</property>
|
||||
<property name="icon-name">pan-down-symbolic</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
<class name="circular" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="fullscreen_button">
|
||||
<property name="halign">end</property>
|
||||
<property name="action-name">win.toggle-fullscreen</property>
|
||||
<property name="tooltip-text" translatable="yes">Toggle Fullscreen</property>
|
||||
<style>
|
||||
<class name="osd" />
|
||||
<class name="circular" />
|
||||
<class name="flat" />
|
||||
</style>
|
||||
<property name="popover">
|
||||
<!--
|
||||
Use manual implementation instead of GtkPopoverMenu because:
|
||||
1. We need to specify the shortcuts manually since called from the menu the zoom does not aim for the cursor.
|
||||
2. Hovering other menu entries would steal the focus from the GtkEntry.
|
||||
-->
|
||||
<object class="GtkPopover">
|
||||
<property name="default-widget">submit_zoom</property>
|
||||
<style>
|
||||
<class name="menu" />
|
||||
</style>
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="margin-top">6</property>
|
||||
<property name="margin-end">6</property>
|
||||
<property name="margin-bottom">6</property>
|
||||
<property name="margin-start">6</property>
|
||||
<property name="spacing">6</property>
|
||||
<child>
|
||||
<object class="GtkListBox" id="zoom_to_list">
|
||||
<child>
|
||||
<object class="GtkListBoxRow" id="zoom_to_300">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<property name="label">300 %</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">end</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="label">Ctrl+3</property>
|
||||
<style>
|
||||
<class name="dim-label" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBoxRow" id="zoom_to_200">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<property name="label">200 %</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">end</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="label">Ctrl+2</property>
|
||||
<style>
|
||||
<class name="dim-label" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBoxRow" id="zoom_to_100">
|
||||
<property name="child">
|
||||
<object class="GtkBox">
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<property name="label">100 %</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">end</property>
|
||||
<property name="hexpand">true</property>
|
||||
<property name="label">Ctrl+1</property>
|
||||
<style>
|
||||
<class name="dim-label" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBoxRow" id="zoom_to_66">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<property name="label">66 %</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBoxRow" id="zoom_to_50">
|
||||
<property name="child">
|
||||
<object class="GtkLabel">
|
||||
<property name="halign">start</property>
|
||||
<property name="label">50 %</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkEntry" id="zoom_value">
|
||||
<property name="activates-default">True</property>
|
||||
<property name="input-purpose">number</property>
|
||||
<property name="max-width-chars">8</property>
|
||||
<property name="tooltip-text">Custom Zoom Level</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton" id="submit_zoom">
|
||||
<property name="icon-name">checkmark-symbolic</property>
|
||||
<property name="tooltip-text">Zoom to Specified Level</property>
|
||||
<style>
|
||||
<class name="suggested-action" />
|
||||
<class name="circular" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
|
|
@ -100,4 +266,4 @@
|
|||
<class name="image-view" />
|
||||
</style>
|
||||
</template>
|
||||
</interface>
|
||||
</interface>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue