general: Add fullscreen widget
This commit is contained in:
parent
422750949c
commit
230ffd5c77
6 changed files with 264 additions and 115 deletions
|
|
@ -22,6 +22,8 @@ src/widgets.rs
|
||||||
src/widgets/drag_overlay.rs
|
src/widgets/drag_overlay.rs
|
||||||
src/widgets/error_details.rs
|
src/widgets/error_details.rs
|
||||||
src/widgets/error_details.ui
|
src/widgets/error_details.ui
|
||||||
|
src/widgets/fullscreen_widget.rs
|
||||||
|
src/widgets/fullscreen_widget.ui
|
||||||
src/widgets/image.rs
|
src/widgets/image.rs
|
||||||
src/widgets/image/background_color.rs
|
src/widgets/image/background_color.rs
|
||||||
src/widgets/image/drag.rs
|
src/widgets/image/drag.rs
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
|
|
||||||
mod drag_overlay;
|
mod drag_overlay;
|
||||||
mod error_details;
|
mod error_details;
|
||||||
|
mod fullscreen_widget;
|
||||||
mod image;
|
mod image;
|
||||||
mod image_page;
|
mod image_page;
|
||||||
mod image_view;
|
mod image_view;
|
||||||
|
|
@ -30,6 +31,7 @@ mod sliding_view;
|
||||||
|
|
||||||
pub use drag_overlay::LpDragOverlay;
|
pub use drag_overlay::LpDragOverlay;
|
||||||
pub use error_details::LpErrorDetails;
|
pub use error_details::LpErrorDetails;
|
||||||
|
pub use fullscreen_widget::LpFullscreenWidget;
|
||||||
pub use image::LpImage;
|
pub use image::LpImage;
|
||||||
pub use image_page::LpImagePage;
|
pub use image_page::LpImagePage;
|
||||||
pub use image_view::LpImageView;
|
pub use image_view::LpImageView;
|
||||||
|
|
|
||||||
147
src/widgets/fullscreen_widget.rs
Normal file
147
src/widgets/fullscreen_widget.rs
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
//
|
||||||
|
// 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<adw::ToolbarView>,
|
||||||
|
#[template_child]
|
||||||
|
pub(super) header_container: TemplateChild<LpShyBin>,
|
||||||
|
|
||||||
|
#[property(get, set, nullable)]
|
||||||
|
headerbar: RefCell<Option<gtk::Widget>>,
|
||||||
|
#[property(get, set, nullable)]
|
||||||
|
content: RefCell<Option<gtk::Widget>>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
fullscreened: Cell<bool>,
|
||||||
|
#[property(get, set)]
|
||||||
|
extend_content: Cell<bool>,
|
||||||
|
#[property(get = LpFullscreenWidget::is_content_extended_to_top, type = bool)]
|
||||||
|
_is_content_extended_to_top: (),
|
||||||
|
#[property(get, set)]
|
||||||
|
is_empty: Cell<bool>,
|
||||||
|
#[property(get, set)]
|
||||||
|
headerbar_opacity: Cell<f64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Self>) {
|
||||||
|
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<imp::LpFullscreenWidget>)
|
||||||
|
@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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/widgets/fullscreen_widget.ui
Normal file
30
src/widgets/fullscreen_widget.ui
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<interface>
|
||||||
|
<template class="LpFullscreenWidget" parent="AdwBin">
|
||||||
|
<binding name="fullscreened">
|
||||||
|
<lookup name="fullscreened" type="GtkWindow">
|
||||||
|
<lookup name="root">LpFullscreenWidget</lookup>
|
||||||
|
</lookup>
|
||||||
|
</binding>
|
||||||
|
<binding name="is-empty">
|
||||||
|
<lookup name="is-empty" type="LpWindow">
|
||||||
|
<lookup name="root">LpFullscreenWidget</lookup>
|
||||||
|
</lookup>
|
||||||
|
</binding>
|
||||||
|
<binding name="headerbar-opacity">
|
||||||
|
<lookup name="headerbar-opacity" type="LpWindow">
|
||||||
|
<lookup name="root">LpFullscreenWidget</lookup>
|
||||||
|
</lookup>
|
||||||
|
</binding>
|
||||||
|
<property name="child">
|
||||||
|
<object class="AdwToolbarView" id="toolbar_view">
|
||||||
|
<child type="top">
|
||||||
|
<object class="LpShyBin" id="header_container">
|
||||||
|
<property name="child" bind-source="LpFullscreenWidget" bind-property="headerbar"/>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<property name="content" bind-source="LpFullscreenWidget" bind-property="content"/>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</template>
|
||||||
|
</interface>
|
||||||
|
|
@ -34,7 +34,7 @@ use std::path::{Path, PathBuf};
|
||||||
use actions::*;
|
use actions::*;
|
||||||
use adw::prelude::*;
|
use adw::prelude::*;
|
||||||
use adw::subclass::prelude::*;
|
use adw::subclass::prelude::*;
|
||||||
use glib::clone;
|
use glib::{clone, Properties};
|
||||||
use gtk::{CompositeTemplate, Widget};
|
use gtk::{CompositeTemplate, Widget};
|
||||||
|
|
||||||
use crate::application::LpApplication;
|
use crate::application::LpApplication;
|
||||||
|
|
@ -42,7 +42,7 @@ use crate::config;
|
||||||
use crate::deps::*;
|
use crate::deps::*;
|
||||||
use crate::util::gettext::*;
|
use crate::util::gettext::*;
|
||||||
use crate::util::Direction;
|
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
|
/// Show window after X milliseconds even if image dimensions are not known yet
|
||||||
const SHOW_WINDOW_AFTER: u64 = 2000;
|
const SHOW_WINDOW_AFTER: u64 = 2000;
|
||||||
|
|
@ -73,14 +73,15 @@ mod imp {
|
||||||
// If some member fields did not implement default,
|
// If some member fields did not implement default,
|
||||||
// we'd need to have a `new()` function in the
|
// we'd need to have a `new()` function in the
|
||||||
// `impl ObjectSubclass for $TYPE` section.
|
// `impl ObjectSubclass for $TYPE` section.
|
||||||
#[derive(Default, Debug, CompositeTemplate)]
|
#[derive(Default, Debug, Properties, CompositeTemplate)]
|
||||||
|
#[properties(wrapper_type = super::LpWindow)]
|
||||||
#[template(file = "window.ui")]
|
#[template(file = "window.ui")]
|
||||||
pub struct LpWindow {
|
pub struct LpWindow {
|
||||||
// Template children are used with the
|
// Template children are used with the
|
||||||
// TemplateChild<T> wrapper, where T is the
|
// TemplateChild<T> wrapper, where T is the
|
||||||
// object type of the template child.
|
// object type of the template child.
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub(super) toolbar_view: TemplateChild<adw::ToolbarView>,
|
pub(super) fullscreen_widget: TemplateChild<LpFullscreenWidget>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub(super) headerbar: TemplateChild<adw::HeaderBar>,
|
pub(super) headerbar: TemplateChild<adw::HeaderBar>,
|
||||||
#[template_child]
|
#[template_child]
|
||||||
|
|
@ -110,6 +111,11 @@ mod imp {
|
||||||
#[template_child]
|
#[template_child]
|
||||||
pub(super) backward_click_gesture: TemplateChild<gtk::GestureClick>,
|
pub(super) backward_click_gesture: TemplateChild<gtk::GestureClick>,
|
||||||
|
|
||||||
|
#[property(get, set)]
|
||||||
|
is_empty: Cell<bool>,
|
||||||
|
#[property(get, set)]
|
||||||
|
headerbar_opacity: Cell<f64>,
|
||||||
|
|
||||||
/// Motion controller for complete window
|
/// Motion controller for complete window
|
||||||
pub(super) motion_controller: gtk::EventControllerMotion,
|
pub(super) motion_controller: gtk::EventControllerMotion,
|
||||||
pub(super) pointer_position: Cell<(f64, f64)>,
|
pub(super) pointer_position: Cell<(f64, f64)>,
|
||||||
|
|
@ -167,6 +173,7 @@ mod imp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[glib::derived_properties]
|
||||||
impl ObjectImpl for LpWindow {
|
impl ObjectImpl for LpWindow {
|
||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
@ -233,12 +240,6 @@ mod imp {
|
||||||
win.resize_default();
|
win.resize_default();
|
||||||
});
|
});
|
||||||
|
|
||||||
self.properties_button.connect_active_notify(glib::clone!(
|
|
||||||
#[weak]
|
|
||||||
obj,
|
|
||||||
move |_| obj.update_headerbar_style()
|
|
||||||
));
|
|
||||||
|
|
||||||
let gesture_click = gtk::GestureClick::new();
|
let gesture_click = gtk::GestureClick::new();
|
||||||
gesture_click.connect_pressed(glib::clone!(
|
gesture_click.connect_pressed(glib::clone!(
|
||||||
#[weak]
|
#[weak]
|
||||||
|
|
@ -339,11 +340,11 @@ mod imp {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.toolbar_view.connect_top_bar_style_notify(glib::clone!(
|
self.image_view
|
||||||
#[weak]
|
.controls_box_start()
|
||||||
obj,
|
.bind_property("opacity", &*self.obj(), "headerbar-opacity")
|
||||||
move |_| obj.on_top_bar_style_notify()
|
.sync_create()
|
||||||
));
|
.build();
|
||||||
|
|
||||||
// action win.previous status
|
// action win.previous status
|
||||||
self.image_view
|
self.image_view
|
||||||
|
|
@ -760,7 +761,7 @@ impl LpWindow {
|
||||||
let current_page = imp.image_view.current_page();
|
let current_page = imp.image_view.current_page();
|
||||||
|
|
||||||
// HeaderBar style
|
// HeaderBar style
|
||||||
self.update_headerbar_style();
|
self.set_is_empty(current_page.is_none());
|
||||||
|
|
||||||
// Window title
|
// Window title
|
||||||
self.update_title();
|
self.update_title();
|
||||||
|
|
@ -896,8 +897,6 @@ impl LpWindow {
|
||||||
.image_view
|
.image_view
|
||||||
.on_fullscreen_changed(self.is_fullscreen());
|
.on_fullscreen_changed(self.is_fullscreen());
|
||||||
|
|
||||||
self.update_headerbar_style();
|
|
||||||
|
|
||||||
if !self.is_fullscreen() {
|
if !self.is_fullscreen() {
|
||||||
self.set_cursor(None);
|
self.set_cursor(None);
|
||||||
self.show_controls();
|
self.show_controls();
|
||||||
|
|
@ -906,39 +905,11 @@ impl LpWindow {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_headerbar_flat(&self) -> bool {
|
fn is_headerbar_flat(&self) -> bool {
|
||||||
matches!(
|
self.imp().fullscreen_widget.is_headerbar_flat()
|
||||||
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.);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_content_extended_to_top(&self) -> bool {
|
fn is_content_extended_to_top(&self) -> bool {
|
||||||
self.is_fullscreen() && !self.imp().properties_button.is_active()
|
self.imp().fullscreen_widget.is_content_extended_to_top()
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_accel_status(&self) {
|
fn update_accel_status(&self) {
|
||||||
|
|
|
||||||
131
src/window.ui
131
src/window.ui
|
|
@ -68,75 +68,72 @@
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</child>
|
||||||
<property name="content">
|
<property name="content">
|
||||||
<object class="AdwToolbarView" id="toolbar_view">
|
<object class="LpFullscreenWidget" id="fullscreen_widget">
|
||||||
<child type="top">
|
<property name="extend-content" bind-source="properties_button" bind-property="active" bind-flags="sync-create|invert-boolean"/>
|
||||||
<object class="LpShyBin">
|
<property name="headerbar">
|
||||||
<property name="child">
|
<object class="AdwHeaderBar" id="headerbar">
|
||||||
<object class="AdwHeaderBar" id="headerbar">
|
<property name="show-end-title-buttons" bind-source="LpWindow" bind-property="fullscreened" bind-flags="sync-create|invert-boolean" />
|
||||||
<property name="show-end-title-buttons" bind-source="LpWindow" bind-property="fullscreened" bind-flags="sync-create|invert-boolean" />
|
<property name="show-start-title-buttons" bind-source="LpWindow" bind-property="fullscreened" bind-flags="sync-create|invert-boolean" />
|
||||||
<property name="show-start-title-buttons" bind-source="LpWindow" bind-property="fullscreened" bind-flags="sync-create|invert-boolean" />
|
<style>
|
||||||
<style>
|
<class name="fullscreen-osd" />
|
||||||
<class name="fullscreen-osd" />
|
</style>
|
||||||
</style>
|
<child type="start">
|
||||||
<child type="start">
|
<object class="GtkButton">
|
||||||
<object class="GtkButton">
|
<property name="icon-name">edit-copy-symbolic</property>
|
||||||
<property name="icon-name">edit-copy-symbolic</property>
|
<property name="action-name">win.copy-image</property>
|
||||||
<property name="action-name">win.copy-image</property>
|
<property name="tooltip-text" translatable="yes">Copy to Clipboard</property>
|
||||||
<property name="tooltip-text" translatable="yes">Copy to Clipboard</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="start">
|
|
||||||
<object class="GtkButton">
|
|
||||||
<property name="icon-name">user-trash-symbolic</property>
|
|
||||||
<property name="action-name">win.trash</property>
|
|
||||||
<property name="tooltip-text" translatable="yes">Move to Trash</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="end">
|
|
||||||
<object class="GtkMenuButton" id="primary_menu">
|
|
||||||
<property name="icon-name">open-menu-symbolic</property>
|
|
||||||
<property name="tooltip-text" translatable="yes">Main Menu</property>
|
|
||||||
<property name="primary">True</property>
|
|
||||||
<property name="popover">
|
|
||||||
<object class="GtkPopoverMenu">
|
|
||||||
<property name="menu-model">primary_menu</property>
|
|
||||||
<child type="rotate-left">
|
|
||||||
<object class="GtkButton">
|
|
||||||
<property name="icon-name">object-rotate-left-symbolic</property>
|
|
||||||
<property name="tooltip-text" translatable="yes">Rotate Left</property>
|
|
||||||
<property name="action-name">win.rotate-ccw</property>
|
|
||||||
<style>
|
|
||||||
<class name="flat" />
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="rotate-right">
|
|
||||||
<object class="GtkButton">
|
|
||||||
<property name="icon-name">object-rotate-right-symbolic</property>
|
|
||||||
<property name="tooltip-text" translatable="yes">Rotate Right</property>
|
|
||||||
<property name="action-name">win.rotate-cw</property>
|
|
||||||
<style>
|
|
||||||
<class name="flat" />
|
|
||||||
</style>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
</object>
|
|
||||||
</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child type="end">
|
|
||||||
<object class="GtkToggleButton" id="properties_button">
|
|
||||||
<property name="icon-name">info-outline-symbolic</property>
|
|
||||||
<property name="tooltip-text" translatable="yes">Image Properties</property>
|
|
||||||
</object>
|
|
||||||
</child>
|
|
||||||
<child>
|
|
||||||
<object class="GtkEventControllerMotion" id="headerbar_events" />
|
|
||||||
</child>
|
|
||||||
</object>
|
</object>
|
||||||
</property>
|
</child>
|
||||||
|
<child type="start">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="icon-name">user-trash-symbolic</property>
|
||||||
|
<property name="action-name">win.trash</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Move to Trash</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="end">
|
||||||
|
<object class="GtkMenuButton" id="primary_menu">
|
||||||
|
<property name="icon-name">open-menu-symbolic</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Main Menu</property>
|
||||||
|
<property name="primary">True</property>
|
||||||
|
<property name="popover">
|
||||||
|
<object class="GtkPopoverMenu">
|
||||||
|
<property name="menu-model">primary_menu</property>
|
||||||
|
<child type="rotate-left">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="icon-name">object-rotate-left-symbolic</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Rotate Left</property>
|
||||||
|
<property name="action-name">win.rotate-ccw</property>
|
||||||
|
<style>
|
||||||
|
<class name="flat" />
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="rotate-right">
|
||||||
|
<object class="GtkButton">
|
||||||
|
<property name="icon-name">object-rotate-right-symbolic</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Rotate Right</property>
|
||||||
|
<property name="action-name">win.rotate-cw</property>
|
||||||
|
<style>
|
||||||
|
<class name="flat" />
|
||||||
|
</style>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
</object>
|
||||||
|
</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child type="end">
|
||||||
|
<object class="GtkToggleButton" id="properties_button">
|
||||||
|
<property name="icon-name">info-outline-symbolic</property>
|
||||||
|
<property name="tooltip-text" translatable="yes">Image Properties</property>
|
||||||
|
</object>
|
||||||
|
</child>
|
||||||
|
<child>
|
||||||
|
<object class="GtkEventControllerMotion" id="headerbar_events" />
|
||||||
|
</child>
|
||||||
</object>
|
</object>
|
||||||
</child>
|
</property>
|
||||||
<property name="content">
|
<property name="content">
|
||||||
<object class="AdwToastOverlay" id="toast_overlay">
|
<object class="AdwToastOverlay" id="toast_overlay">
|
||||||
<property name="child">
|
<property name="child">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue