printing: Add printing support
Part-of: <https://gitlab.gnome.org/Incubator/loupe/-/merge_requests/165>
This commit is contained in:
parent
e96b9ccac9
commit
ae5ca76176
22 changed files with 936 additions and 49 deletions
86
Cargo.lock
generated
86
Cargo.lock
generated
|
|
@ -1542,6 +1542,36 @@ dependencies = [
|
|||
"system-deps",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gvdb"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6fc5915836ff1779eb9455810dad729811334b1026d4316eaf89c3a415e32ea"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"flate2",
|
||||
"lazy_static",
|
||||
"memmap2",
|
||||
"quick-xml",
|
||||
"safe-transmute",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"walkdir",
|
||||
"zvariant",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gvdb-macros"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c5166acbf52c6109ec51fc758532aaa47b0eb8c4becd585c3dde6f5965c6529e"
|
||||
dependencies = [
|
||||
"gvdb",
|
||||
"litrs",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gweather-sys"
|
||||
version = "4.2.0"
|
||||
|
|
@ -1930,6 +1960,15 @@ version = "0.3.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
||||
|
||||
[[package]]
|
||||
name = "litrs"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f17c3668f3cc1132437cdadc93dab05e52d592f06948d3f64828430c36e4a70"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "locale_config"
|
||||
version = "0.3.0"
|
||||
|
|
@ -1975,6 +2014,7 @@ dependencies = [
|
|||
"gettext-rs",
|
||||
"gio",
|
||||
"gtk4",
|
||||
"gvdb-macros",
|
||||
"image",
|
||||
"indexmap",
|
||||
"kamadak-exif",
|
||||
|
|
@ -2040,6 +2080,15 @@ version = "2.5.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.7.1"
|
||||
|
|
@ -2578,6 +2627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "ffc053f057dd768a56f62cd7e434c42c831d296968997e9ac1f76ea7c2d14c41"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -2788,6 +2838,12 @@ dependencies = [
|
|||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041"
|
||||
|
||||
[[package]]
|
||||
name = "safe-transmute"
|
||||
version = "0.11.2"
|
||||
|
|
@ -2803,6 +2859,15 @@ dependencies = [
|
|||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
|
|
@ -2859,6 +2924,17 @@ dependencies = [
|
|||
"syn 2.0.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_repr"
|
||||
version = "0.1.12"
|
||||
|
|
@ -3284,6 +3360,16 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "36df944cda56c7d8d8b7496af378e6b16de9284591917d307c9b4d313c44e698"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.9.0+wasi-snapshot-preview1"
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ anyhow = { version = "1.0.66", features = ["backtrace"] }
|
|||
arc-swap = "1.6.0"
|
||||
env_logger = "0.10.0"
|
||||
futures = "0.3.25"
|
||||
gvdb-macros = "0.1.6"
|
||||
indexmap = "1.9.2"
|
||||
kamadak-exif = "0.5.5"
|
||||
lcms2 = "5.6.0"
|
||||
|
|
|
|||
192
data/gtk/print.ui
Normal file
192
data/gtk/print.ui
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<interface>
|
||||
<template class="LpPrint" parent="AdwWindow">
|
||||
<property name="title" translatable="yes">Layout Image</property>
|
||||
<property name="default-width">350</property>
|
||||
<signal name="notify::orientation" handler="orientation_changed" swapped="yes" />
|
||||
<property name="content">
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="spacing">12</property>
|
||||
<child>
|
||||
<object class="GtkShortcutController">
|
||||
<property name="scope">managed</property>
|
||||
<child>
|
||||
<object class="GtkShortcut">
|
||||
<property name="trigger">Escape|<Ctrl>w</property>
|
||||
<property name="action">action(window.close)</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwHeaderBar">
|
||||
<child type="start">
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">go-previous-symbolic</property>
|
||||
<property name="action-name">print.back</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="flat" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="orientation">vertical</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="spacing">24</property>
|
||||
<property name="margin-top">18</property>
|
||||
<property name="margin-end">18</property>
|
||||
<property name="margin-bottom">18</property>
|
||||
<property name="margin-start">18</property>
|
||||
<child>
|
||||
<object class="LpPrintPreview" id="preview">
|
||||
<property name="css-name">lpprintpreview</property>
|
||||
<binding name="print">
|
||||
<constant>LpPrint</constant>
|
||||
</binding>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<property name="halign">center</property>
|
||||
<property name="spacing">36</property>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<style>
|
||||
<class name="linked" />
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkToggleButton">
|
||||
<property name="icon-name">page-orientation-portrait-symbolic</property>
|
||||
<property name="action-name">print.orientation</property>
|
||||
<property name="action-target">"portrait"</property>
|
||||
<property name="tooltip-text" translatable="yes">Portrait page orientation</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkToggleButton">
|
||||
<property name="icon-name">page-orientation-landscape-symbolic</property>
|
||||
<property name="action-name">print.orientation</property>
|
||||
<property name="action-target">"landscape"</property>
|
||||
<property name="tooltip-text" translatable="yes">Landscape page orientation</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkBox">
|
||||
<style>
|
||||
<class name="linked" />
|
||||
</style>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">center-on-page-horizontal-symbolic</property>
|
||||
<property name="action-name">print.center-horizontally</property>
|
||||
<property name="tooltip-text" translatable="yes">Center image horizontally</property>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="icon-name">center-on-page-vertical-symbolic</property>
|
||||
<property name="action-name">print.center-vertically</property>
|
||||
<property name="tooltip-text" translatable="yes">Center image vertically</property>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkListBox">
|
||||
<property name="selection-mode">none</property>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">Image Width</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="width">
|
||||
<property name="valign">center</property>
|
||||
<property name="max-width-chars">5</property>
|
||||
<signal name="value-changed" handler="width_changed" swapped="yes" />
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">Image Scale</property>
|
||||
<child>
|
||||
<object class="GtkLabel" id="scale">
|
||||
<style>
|
||||
<class name="dim-label" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">Left Margin</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="margin_left">
|
||||
<property name="valign">center</property>
|
||||
<property name="max-width-chars">5</property>
|
||||
<signal name="value-changed" handler="margin_left_changed" swapped="yes" />
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwActionRow">
|
||||
<property name="title" translatable="yes">Top Margin</property>
|
||||
<child>
|
||||
<object class="GtkSpinButton" id="margin_top">
|
||||
<property name="valign">center</property>
|
||||
<property name="max-width-chars">5</property>
|
||||
<signal name="value-changed" handler="margin_top_changed" swapped="yes" />
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="AdwComboRow" id="unit">
|
||||
<property name="title" translatable="yes">Length Unit</property>
|
||||
<signal name="notify::selected-item" handler="unit_selected" swapped="yes" />
|
||||
<property name="model">
|
||||
<object class="GtkStringList">
|
||||
<items>
|
||||
<item>cm</item>
|
||||
<item>in</item>
|
||||
<item>px</item>
|
||||
</items>
|
||||
</object>
|
||||
</property>
|
||||
</object>
|
||||
</child>
|
||||
<style>
|
||||
<class name="boxed-list" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
<child>
|
||||
<object class="GtkButton">
|
||||
<property name="label" translatable="yes">Print</property>
|
||||
<property name="action-name">print.print</property>
|
||||
<property name="halign">center</property>
|
||||
<property name="valign">end</property>
|
||||
<property name="vexpand">true</property>
|
||||
<property name="width-request">160</property>
|
||||
<style>
|
||||
<class name="suggested-action" />
|
||||
<class name="pill" />
|
||||
</style>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</child>
|
||||
</object>
|
||||
</property>
|
||||
</template>
|
||||
</interface>
|
||||
|
|
@ -1,8 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<gresources>
|
||||
<gresource prefix="/org/gnome/Loupe">
|
||||
<file compressed="true" preprocess="xml-stripblanks">gtk/help_overlay.ui</file>
|
||||
<file>style.css</file>
|
||||
<file>style-hc.css</file>
|
||||
</gresource>
|
||||
</gresources>
|
||||
|
|
@ -38,29 +38,8 @@ appstream_file = i18n.merge_file(
|
|||
install_dir: iv_datadir / 'metainfo'
|
||||
)
|
||||
|
||||
# appstream_util = find_program('appstream-util', required: false)
|
||||
# if appstream_util.found()
|
||||
# test('Validate appstream file', appstream_util,
|
||||
# args: ['validate', appstream_file]
|
||||
# )
|
||||
# endif
|
||||
|
||||
install_data('org.gnome.Loupe.gschema.xml',
|
||||
install_dir: iv_datadir / 'glib-2.0' / 'schemas'
|
||||
)
|
||||
|
||||
compile_schemas = find_program('glib-compile-schemas', required: false)
|
||||
if compile_schemas.found()
|
||||
test('Validate schema file', compile_schemas,
|
||||
args: ['--strict', '--dry-run', meson.current_source_dir()]
|
||||
)
|
||||
endif
|
||||
|
||||
gnome.compile_resources('loupe',
|
||||
'loupe.gresource.xml',
|
||||
gresource_bundle: true,
|
||||
install: true,
|
||||
install_dir: iv_pkgdatadir,
|
||||
)
|
||||
|
||||
subdir('icons')
|
||||
|
||||
# Post install
|
||||
|
||||
gnome.post_install()
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<schemalist gettext-domain="loupe">
|
||||
<schema id="org.gnome.Loupe" path="/org/gnome/Loupe/">
|
||||
</schema>
|
||||
</schemalist>
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 3 0 c -1.644531 0 -3 1.355469 -3 3 v 10.003906 c 0 1.644532 1.355469 3 3 3 h 10 c 1.644531 0 3 -1.355468 3 -3 v -8.503906 c 0 -0.265625 -0.105469 -0.519531 -0.292969 -0.707031 l -3.5 -3.5 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 2 h 8 v 1.5 c 0 1 0.5 1.5 1.5 1.5 h 1.5 v 8.003906 c 0 0.574219 -0.429688 1 -1 1 h -10 c -0.570312 0 -1 -0.425781 -1 -1 v -10.003906 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/>
|
||||
<path d="m 7 6 h 2 v -2 h -2 z m 0 0"/>
|
||||
<path d="m 7 9 h 2 v -2 h -2 z m 0 0"/>
|
||||
<path d="m 7 12 h 2 v -2 h -2 z m 0 0"/>
|
||||
<g fill-opacity="0.34902">
|
||||
<path d="m 10 12 h 2 v -2 h -2 z m 0 0"/>
|
||||
<path d="m 4 12 h 2 v -2 h -2 z m 0 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 848 B |
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 3 0 c -1.644531 0 -3 1.355469 -3 3 v 10.003906 c 0 1.644532 1.355469 3 3 3 h 10 c 1.644531 0 3 -1.355468 3 -3 v -8.503906 c 0 -0.265625 -0.105469 -0.519531 -0.292969 -0.707031 l -3.5 -3.5 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 2 h 8 v 1.5 c 0 1 0.5 1.5 1.5 1.5 h 1.5 v 8.003906 c 0 0.574219 -0.429688 1 -1 1 h -10 c -0.570312 0 -1 -0.425781 -1 -1 v -10.003906 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/>
|
||||
<path d="m 10 7 v 2 h 2 v -2 z m 0 0"/>
|
||||
<path d="m 7 7 v 2 h 2 v -2 z m 0 0"/>
|
||||
<path d="m 4 7 v 2 h 2 v -2 z m 0 0"/>
|
||||
<g fill-opacity="0.34902">
|
||||
<path d="m 4 10 v 2 h 2 v -2 z m 0 0"/>
|
||||
<path d="m 4 4 v 2 h 2 v -2 z m 0 0"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 841 B |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 15 11 c 0 1.644531 -1.355469 3 -3 3 h -9 c -1.644531 0 -3 -1.355469 -3 -3 v -6 c 0 -1.644531 1.355469 -3 3 -3 h 7.5 c 0.265625 0 0.519531 0.105469 0.707031 0.292969 l 3.5 3.5 c 0.1875 0.1875 0.292969 0.441406 0.292969 0.707031 z m -2 0 v -4 h -1.5 c -1 0 -1.5 -0.5 -1.5 -1.5 v -1.5 h -7 c -0.570312 0 -1 0.429688 -1 1 v 6 c 0 0.570312 0.429688 1 1 1 h 9 c 0.570312 0 1 -0.429688 1 -1 z m -8.5 -6.035156 c -0.824219 0 -1.5 0.675781 -1.5 1.5 c 0 0.820312 0.675781 1.5 1.5 1.5 s 1.5 -0.679688 1.5 -1.5 c 0 -0.824219 -0.675781 -1.5 -1.5 -1.5 z m 0 1 c 0.28125 0 0.5 0.21875 0.5 0.5 s -0.21875 0.5 -0.5 0.5 s -0.5 -0.21875 -0.5 -0.5 s 0.21875 -0.5 0.5 -0.5 z m 0 0"/>
|
||||
<path d="m 13.460938 10 h -2.417969 c -0.558594 0 -1.09375 -0.195312 -1.523438 -0.550781 l -1.128906 -0.941407 c -0.804687 -0.671874 -1.972656 -0.675781 -2.78125 -0.003906 l -1.09375 0.898438 c -0.449219 0.375 -1.015625 0.578125 -1.597656 0.578125 h -1.339844 c -0.277344 0 -0.5 0.222656 -0.5 0.5 c 0 0.273437 0.222656 0.5 0.5 0.5 h 1.339844 c 0.816406 0 1.605469 -0.285157 2.234375 -0.804688 l 1.09375 -0.902343 c 0.4375 -0.363282 1.070312 -0.359376 1.503906 0.003906 l 1.128906 0.941406 c 0.609375 0.503906 1.371094 0.78125 2.164063 0.78125 h 2.417969 c 0.277343 0 0.5 -0.222656 0.5 -0.5 s -0.222657 -0.5 -0.5 -0.5 z m 0 0"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg height="16px" viewBox="0 0 16 16" width="16px" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m 5 1 c -1.644531 0 -3 1.355469 -3 3 v 9 c 0 1.644531 1.355469 3 3 3 h 6 c 1.644531 0 3 -1.355469 3 -3 v -7.5 c 0 -0.265625 -0.105469 -0.519531 -0.292969 -0.707031 l -3.5 -3.5 c -0.1875 -0.1875 -0.441406 -0.292969 -0.707031 -0.292969 z m 0 2 h 4 v 1.5 c 0 1 0.5 1.5 1.5 1.5 h 1.5 v 7 c 0 0.570312 -0.429688 1 -1 1 h -6 c -0.570312 0 -1 -0.429688 -1 -1 v -9 c 0 -0.570312 0.429688 -1 1 -1 z m 0 0"/>
|
||||
<path d="m 8 6 c -1.097656 0 -2 0.902344 -2 2 c 0 0.53125 0.210938 1.015625 0.550781 1.375 c -0.015625 0.011719 -0.03125 0.015625 -0.050781 0.027344 c -0.925781 0.535156 -1.5 1.527344 -1.5 2.597656 c 0 0.542969 0.441406 0.980469 0.980469 0.980469 h 4.039062 c 0.539063 0 0.980469 -0.4375 0.980469 -0.980469 c 0 -1.070312 -0.574219 -2.0625 -1.5 -2.597656 c -0.015625 -0.011719 -0.035156 -0.019532 -0.054688 -0.027344 c 0.34375 -0.359375 0.554688 -0.84375 0.554688 -1.375 c 0 -1.097656 -0.902344 -2 -2 -2 z m 0 1 c 0.558594 0 1 0.441406 1 1 s -0.441406 1 -1 1 s -1 -0.441406 -1 -1 s 0.441406 -1 1 -1 z m 0 3 c 0.34375 0 0.691406 0.089844 1 0.269531 c 0.566406 0.324219 0.933594 0.902344 0.992188 1.542969 c 0.007812 0.105469 -0.074219 0.1875 -0.179688 0.1875 h -3.605469 c -0.113281 0 -0.207031 -0.09375 -0.195312 -0.207031 c 0.0625 -0.632813 0.429687 -1.203125 0.988281 -1.523438 c 0.308594 -0.179687 0.65625 -0.269531 1 -0.269531 z m 0 0" fill-rule="evenodd"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -22,5 +22,11 @@ row.property label.subtitle {
|
|||
margin: 16px;
|
||||
border-radius: 5px;
|
||||
outline: 2px solid white;
|
||||
box-shadow: 0 0 10px rgba(0.4, 0.4, 0.4, 0.8);
|
||||
box-shadow: 0 0 10px rgba(0, 0, 0, 200);
|
||||
}
|
||||
|
||||
lpprintpreview {
|
||||
background-color: rgb(240, 240, 240);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 0 14px 1px black;
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
pub const APP_ID: &str = "@APP_ID@";
|
||||
pub const PROFILE: &str = "@PROFILE@";
|
||||
pub const PKGDATADIR: &str = "@pkgdatadir@";
|
||||
pub const VERSION: &str = "@VERSION@";
|
||||
pub const LOCALEDIR: &str = "@localedir@";
|
||||
|
|
|
|||
|
|
@ -86,6 +86,11 @@ impl Tile {
|
|||
let area = round(&self.area().scale(zoom, zoom));
|
||||
let area_with_bleed = round(&self.area_with_bleed().scale(zoom, zoom));
|
||||
|
||||
if area_with_bleed.width() < 1. || area_with_bleed.height() < 1. {
|
||||
log::warn!("Trying to draw image with dimensions smaller than 1");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: do not clip outer bounderies of the image
|
||||
snapshot.push_clip(&area);
|
||||
if let Some(background_color) = &options.background_color {
|
||||
|
|
|
|||
|
|
@ -65,6 +65,9 @@ mod deps {
|
|||
use application::LpApplication;
|
||||
use deps::*;
|
||||
|
||||
static GRESOURCE_BYTES: &[u8] =
|
||||
gvdb_macros::include_gresource_from_dir!("/org/gnome/Loupe", "data/resources");
|
||||
|
||||
fn main() -> glib::ExitCode {
|
||||
env_logger::Builder::from_default_env()
|
||||
.format_timestamp_millis()
|
||||
|
|
@ -74,9 +77,9 @@ fn main() -> glib::ExitCode {
|
|||
bindtextdomain("loupe", config::LOCALEDIR).unwrap();
|
||||
textdomain("loupe").unwrap();
|
||||
|
||||
let res = gio::Resource::load(config::PKGDATADIR.to_owned() + "/loupe.gresource")
|
||||
.expect("Could not load resources");
|
||||
gio::resources_register(&res);
|
||||
gio::resources_register(
|
||||
&gio::Resource::from_data(&glib::Bytes::from_static(GRESOURCE_BYTES)).unwrap(),
|
||||
);
|
||||
|
||||
LpApplication::new().run()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -999,6 +999,34 @@ impl LpImage {
|
|||
snapshot.to_paintable(None)
|
||||
}
|
||||
|
||||
pub fn print_data(&self, scale: f64) -> Option<gdk::Texture> {
|
||||
let orientation = self.metadata().orientation();
|
||||
|
||||
let render_options = tiling::RenderOptions {
|
||||
scaling_filter: gsk::ScalingFilter::Trilinear,
|
||||
background_color: None,
|
||||
scale_factor: 1,
|
||||
};
|
||||
|
||||
let snapshot = gtk::Snapshot::new();
|
||||
|
||||
self.snapshot_rotate_mirror(
|
||||
&snapshot,
|
||||
-orientation.rotation as f32,
|
||||
orientation.mirrored,
|
||||
);
|
||||
|
||||
self.imp()
|
||||
.frame_buffer
|
||||
.load()
|
||||
.add_to_snapshot(&snapshot, scale, &render_options);
|
||||
|
||||
let node = snapshot.to_node()?;
|
||||
let renderer = self.root()?.renderer();
|
||||
|
||||
Some(renderer.render_texture(&node, None))
|
||||
}
|
||||
|
||||
fn mirrored(&self) -> bool {
|
||||
self.imp().mirrored.get()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ use crate::deps::*;
|
|||
use crate::file_model::LpFileModel;
|
||||
use crate::util::gettext::*;
|
||||
use crate::util::{Direction, Position};
|
||||
use crate::widgets::{LpImage, LpImagePage, LpSlidingView};
|
||||
use crate::widgets::{LpImage, LpImagePage, LpPrint, LpSlidingView};
|
||||
|
||||
use crate::util::spawn;
|
||||
use adw::prelude::*;
|
||||
|
|
@ -609,6 +609,17 @@ impl LpImageView {
|
|||
}
|
||||
|
||||
pub fn print(&self) -> anyhow::Result<()> {
|
||||
let image = self
|
||||
.current_image()
|
||||
.context("No current image for printing")?;
|
||||
|
||||
let root = self.root().context("Could not get root for widget")?;
|
||||
let window = root
|
||||
.downcast_ref::<gtk::Window>()
|
||||
.context("Could not downcast to GtkWindow")?;
|
||||
|
||||
LpPrint::new(image, window.clone(), None, None).run();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -20,11 +20,15 @@
|
|||
mod image;
|
||||
mod image_page;
|
||||
mod image_view;
|
||||
mod print;
|
||||
mod print_preview;
|
||||
mod properties_view;
|
||||
mod sliding_view;
|
||||
|
||||
pub use image::LpImage;
|
||||
pub use image_page::LpImagePage;
|
||||
pub use image_view::LpImageView;
|
||||
pub use print::LpPrint;
|
||||
pub use print_preview::LpPrintPreview;
|
||||
pub use properties_view::LpPropertiesView;
|
||||
pub use sliding_view::LpSlidingView;
|
||||
|
|
|
|||
437
src/widgets/print.rs
Normal file
437
src/widgets/print.rs
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
//! The print layout widget that also coordinates the print process
|
||||
|
||||
use crate::deps::*;
|
||||
use crate::widgets::{LpImage, LpPrintPreview};
|
||||
|
||||
use adw::prelude::*;
|
||||
use adw::subclass::prelude::*;
|
||||
use glib::Properties;
|
||||
use gtk::CompositeTemplate;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, CompositeTemplate, Properties)]
|
||||
#[template(file = "../../data/gtk/print.ui")]
|
||||
#[properties(wrapper_type = super::LpPrint)]
|
||||
pub struct LpPrint {
|
||||
#[template_child]
|
||||
pub(super) preview: TemplateChild<LpPrintPreview>,
|
||||
|
||||
// ListBox entries
|
||||
#[template_child]
|
||||
pub(super) width: TemplateChild<gtk::SpinButton>,
|
||||
#[template_child]
|
||||
pub(super) scale: TemplateChild<gtk::Label>,
|
||||
#[template_child]
|
||||
pub(super) margin_left: TemplateChild<gtk::SpinButton>,
|
||||
#[template_child]
|
||||
pub(super) margin_top: TemplateChild<gtk::SpinButton>,
|
||||
#[template_child]
|
||||
pub(super) unit: TemplateChild<adw::ComboRow>,
|
||||
|
||||
#[property(get, set, builder().construct_only())]
|
||||
pub(super) image: OnceCell<LpImage>,
|
||||
#[property(get, set, builder().construct_only())]
|
||||
pub(super) parent_window: OnceCell<gtk::Window>,
|
||||
#[property(get, set, builder().construct_only())]
|
||||
pub(super) print_settings: OnceCell<gtk::PrintSettings>,
|
||||
#[property(get)]
|
||||
pub(super) print_operation: gtk::PrintOperation,
|
||||
|
||||
#[property(get, set)]
|
||||
pub(super) orientation: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
pub(super) unit_factor: Cell<f64>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for LpPrint {
|
||||
const NAME: &'static str = "LpPrint";
|
||||
type Type = super::LpPrint;
|
||||
type ParentType = adw::Window;
|
||||
|
||||
fn class_init(klass: &mut Self::Class) {
|
||||
Self::bind_template(klass);
|
||||
Self::Type::bind_template_callbacks(klass);
|
||||
|
||||
klass.install_action_async("print.print", None, |print, _, _| async move {
|
||||
print.print();
|
||||
});
|
||||
|
||||
klass.install_action_async("print.back", None, |print, _, _| async move {
|
||||
print.back().await;
|
||||
});
|
||||
|
||||
klass.install_action("print.center-horizontally", None, |print, _, _| {
|
||||
print.center_horizontally();
|
||||
});
|
||||
|
||||
klass.install_action("print.center-vertically", None, |print, _, _| {
|
||||
print.center_vertically();
|
||||
});
|
||||
|
||||
klass.install_property_action("print.orientation", "orientation");
|
||||
}
|
||||
|
||||
fn instance_init(obj: &glib::subclass::InitializingObject<Self>) {
|
||||
obj.init_template();
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for LpPrint {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
|
||||
let obj = self.obj();
|
||||
|
||||
obj.set_unit_factor(1.);
|
||||
|
||||
obj.set_transient_for(Some(&obj.parent_window()));
|
||||
obj.set_modal(true);
|
||||
|
||||
self.print_operation
|
||||
.set_print_settings(Some(&obj.print_settings()));
|
||||
self.print_operation.set_allow_async(true);
|
||||
self.print_operation.set_n_pages(1);
|
||||
self.print_operation.set_current_page(0);
|
||||
|
||||
if let Some(uri) = obj.image().file().map(|x| x.uri()) {
|
||||
obj.print_settings()
|
||||
.set(gtk::PRINT_SETTINGS_OUTPUT_URI, Some(&format!("{uri}.pdf")));
|
||||
}
|
||||
|
||||
self.print_operation.connect_begin_print(move |op, _ctx| {
|
||||
op.set_n_pages(1);
|
||||
});
|
||||
|
||||
self.print_operation.connect_draw_page(
|
||||
glib::clone!(@weak obj => move |_operation, _context, _page_nr| {
|
||||
let imp = obj.imp();
|
||||
|
||||
imp.width.set_increments(1., 5.);
|
||||
imp.margin_left.set_increments(1., 5.);
|
||||
imp.margin_top.set_increments(1., 5.);
|
||||
|
||||
obj.set_ranges();
|
||||
|
||||
imp.width.set_value(obj.image().image_size().0 as f64);
|
||||
obj.center_horizontally();
|
||||
|
||||
obj.unit_selected();
|
||||
|
||||
imp.print_operation.cancel();
|
||||
|
||||
let orientation = match obj.page_setup().orientation() {
|
||||
gtk::PageOrientation::Portrait => "portrait",
|
||||
gtk::PageOrientation::Landscape => "landscape",
|
||||
_ => "other",
|
||||
};
|
||||
obj.set_orientation(orientation);
|
||||
|
||||
obj.set_visible(true);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
Self::derived_properties()
|
||||
}
|
||||
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
self.derived_set_property(id, value, pspec)
|
||||
}
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for LpPrint {}
|
||||
impl WindowImpl for LpPrint {}
|
||||
impl AdwWindowImpl for LpPrint {}
|
||||
}
|
||||
glib::wrapper! {
|
||||
pub struct LpPrint(ObjectSubclass<imp::LpPrint>)
|
||||
@extends gtk::Widget, gtk::Window, adw::Window,
|
||||
@implements gtk::Buildable, gtk::ConstraintTarget;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl LpPrint {
|
||||
pub fn new(
|
||||
image: LpImage,
|
||||
parent_window: gtk::Window,
|
||||
print_settings: Option<gtk::PrintSettings>,
|
||||
page_setup: Option<gtk::PageSetup>,
|
||||
) -> Self {
|
||||
let print_settings = print_settings.unwrap_or_default();
|
||||
|
||||
let obj: Self = glib::Object::builder()
|
||||
.property("image", image)
|
||||
.property("print-settings", print_settings)
|
||||
.property("parent-window", parent_window)
|
||||
.build();
|
||||
|
||||
if let Some(page_setup) = page_setup {
|
||||
obj.print_operation()
|
||||
.set_default_page_setup(Some(&page_setup));
|
||||
}
|
||||
|
||||
obj
|
||||
}
|
||||
|
||||
pub fn unit(&self) -> glib::GString {
|
||||
self.imp()
|
||||
.unit
|
||||
.selected_item()
|
||||
.and_downcast::<gtk::StringObject>()
|
||||
.map(|x| x.string())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
pub fn dpi(&self) -> f64 {
|
||||
self.print_settings().resolution() as f64
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn width_changed(&self) {
|
||||
self.imp()
|
||||
.scale
|
||||
.set_label(&format!("{:.2}\u{202F}%", self.user_scale() * 100.));
|
||||
|
||||
self.draw_preview();
|
||||
}
|
||||
|
||||
/// Returns user selected image width in pixels
|
||||
pub fn user_width(&self) -> f64 {
|
||||
f64::max(1., self.imp().width.value() / self.unit_factor())
|
||||
}
|
||||
|
||||
/// Returns scaling of the original image based on selected image size
|
||||
pub fn user_scale(&self) -> f64 {
|
||||
let (orig_width, _) = self.image().image_size();
|
||||
|
||||
self.user_width() / orig_width as f64
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn margin_left_changed(&self) {
|
||||
self.draw_preview();
|
||||
}
|
||||
|
||||
/// Returns user selected left margin in pixels
|
||||
pub fn user_margin_left(&self) -> f64 {
|
||||
self.imp().margin_left.value() / self.unit_factor()
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn margin_top_changed(&self) {
|
||||
self.draw_preview();
|
||||
}
|
||||
|
||||
/// Returns user selected top margin in pixels
|
||||
pub fn user_margin_top(&self) -> f64 {
|
||||
self.imp().margin_top.value() / self.unit_factor()
|
||||
}
|
||||
|
||||
/// Redraws the preview widget child
|
||||
fn draw_preview(&self) {
|
||||
self.imp().preview.queue_resize();
|
||||
}
|
||||
|
||||
/// Centers the image horizontally
|
||||
fn center_horizontally(&self) {
|
||||
let margin_left =
|
||||
(self.page_setup().paper_width(gtk::Unit::Inch) * self.dpi() - self.user_width()) / 2.;
|
||||
|
||||
self.imp()
|
||||
.margin_left
|
||||
.set_value(margin_left * self.unit_factor());
|
||||
}
|
||||
|
||||
/// Centers the image vertically
|
||||
fn center_vertically(&self) {
|
||||
let user_height = self.image().image_size().1 as f64 * self.user_scale();
|
||||
let margin_top =
|
||||
(self.page_setup().paper_height(gtk::Unit::Inch) * self.dpi() - user_height) / 2.;
|
||||
|
||||
self.imp()
|
||||
.margin_top
|
||||
.set_value(margin_top * self.unit_factor());
|
||||
}
|
||||
|
||||
/// Sets the allowed ranges for the spin button based on current unit
|
||||
fn set_ranges(&self) {
|
||||
let imp = self.imp();
|
||||
|
||||
let max_height =
|
||||
self.page_setup().page_height(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
|
||||
|
||||
let max_width =
|
||||
self.page_setup().page_width(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
|
||||
imp.width.set_range(self.unit_factor(), max_width);
|
||||
|
||||
let min_left_margin =
|
||||
self.page_setup().left_margin(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
|
||||
imp.margin_left
|
||||
.set_range(min_left_margin, min_left_margin + max_width);
|
||||
|
||||
let min_top_margin =
|
||||
self.page_setup().top_margin(gtk::Unit::Inch) * self.dpi() * self.unit_factor();
|
||||
imp.margin_top
|
||||
.set_range(min_top_margin, min_top_margin + max_height);
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn orientation_changed(&self) {
|
||||
let orientation = if self.orientation() == "landscape" {
|
||||
gtk::PageOrientation::Landscape
|
||||
} else {
|
||||
gtk::PageOrientation::Portrait
|
||||
};
|
||||
self.page_setup().set_orientation(orientation);
|
||||
self.set_ranges();
|
||||
self.draw_preview();
|
||||
}
|
||||
|
||||
#[template_callback]
|
||||
pub fn unit_selected(&self) {
|
||||
let imp = self.imp();
|
||||
|
||||
let (unit_factor, digits) = match self.unit().as_str() {
|
||||
"cm" => (2.54 / self.dpi(), 2),
|
||||
"in" => (1. / self.dpi(), 2),
|
||||
"px" => (1., 0),
|
||||
unit => {
|
||||
log::error!("Unknown unit '{unit}'");
|
||||
(1., 2)
|
||||
}
|
||||
};
|
||||
|
||||
let update_factor = unit_factor / self.unit_factor();
|
||||
let width = imp.width.value() * update_factor;
|
||||
let margin_left = imp.margin_left.value() * update_factor;
|
||||
let margin_top = imp.margin_top.value() * update_factor;
|
||||
|
||||
self.set_unit_factor(unit_factor);
|
||||
self.set_ranges();
|
||||
|
||||
imp.width.set_digits(digits);
|
||||
imp.width.set_value(width);
|
||||
|
||||
imp.margin_left.set_digits(digits);
|
||||
imp.margin_left.set_value(margin_left);
|
||||
|
||||
imp.margin_top.set_digits(digits);
|
||||
imp.margin_top.set_value(margin_top);
|
||||
}
|
||||
|
||||
pub fn run(&self) {
|
||||
self.print_operation()
|
||||
.run(
|
||||
gtk::PrintOperationAction::PrintDialog,
|
||||
self.imp().parent_window.get(),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn page_setup(&self) -> gtk::PageSetup {
|
||||
self.print_operation().default_page_setup()
|
||||
}
|
||||
|
||||
/// Go back to print dialog from image layout
|
||||
async fn back(&self) {
|
||||
self.close();
|
||||
let print = Self::new(
|
||||
self.image(),
|
||||
self.parent_window(),
|
||||
self.print_operation().print_settings(),
|
||||
Some(self.print_operation().default_page_setup()),
|
||||
);
|
||||
print.run();
|
||||
}
|
||||
|
||||
/// Initialize actual print operation
|
||||
fn print(&self) {
|
||||
self.close();
|
||||
|
||||
let print_operation = gtk::PrintOperation::new();
|
||||
|
||||
let print_settings = self.print_operation().print_settings();
|
||||
|
||||
if let Some(print_settings) = &print_settings {
|
||||
if let Some(uri) = self.image().file().map(|x| x.uri()) {
|
||||
print_settings.set(gtk::PRINT_SETTINGS_OUTPUT_URI, Some(&format!("{uri}.pdf")));
|
||||
}
|
||||
}
|
||||
|
||||
print_operation.set_print_settings(print_settings.as_ref());
|
||||
print_operation.set_default_page_setup(Some(&self.print_operation().default_page_setup()));
|
||||
|
||||
print_operation.connect_begin_print(move |op, _ctx| {
|
||||
op.set_n_pages(1);
|
||||
});
|
||||
|
||||
print_operation.connect_draw_page(
|
||||
glib::clone!(@weak self as obj => move |_operation, context, _page_nr| {
|
||||
obj.draw_page(context);
|
||||
}),
|
||||
);
|
||||
|
||||
print_operation
|
||||
.run(
|
||||
gtk::PrintOperationAction::Print,
|
||||
Some(&self.parent_window()),
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// Draw PDF for printing
|
||||
fn draw_page(&self, print_context: >k::PrintContext) {
|
||||
log::debug!("Drawing image to print");
|
||||
let image = self.image();
|
||||
|
||||
let cairo_dpi = print_context.dpi_x();
|
||||
|
||||
let (orig_width, _) = image.image_size();
|
||||
|
||||
let texture_scale = self.user_width() / orig_width as f64;
|
||||
let cairo_scale = cairo_dpi / self.dpi();
|
||||
|
||||
let texture = image.print_data(texture_scale).unwrap();
|
||||
|
||||
let mut downloader = gdk::TextureDownloader::new(&texture);
|
||||
downloader.set_format(gdk::MemoryFormat::B8g8r8a8Premultiplied);
|
||||
|
||||
let (data, stride) = downloader.download_bytes();
|
||||
let data = data.to_vec();
|
||||
|
||||
let width = texture.width();
|
||||
let height = texture.height();
|
||||
|
||||
let cairo_surface = cairo::ImageSurface::create_for_data(
|
||||
data,
|
||||
cairo::Format::ARgb32,
|
||||
width,
|
||||
height,
|
||||
stride as i32,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let cairo_context = print_context.cairo_context();
|
||||
cairo_context.scale(cairo_scale, cairo_scale);
|
||||
|
||||
let margin_left =
|
||||
self.user_margin_left() - self.page_setup().left_margin(gtk::Unit::Inch) * self.dpi();
|
||||
let margin_top =
|
||||
self.user_margin_top() - self.page_setup().top_margin(gtk::Unit::Inch) * self.dpi();
|
||||
|
||||
cairo_context
|
||||
.set_source_surface(cairo_surface, margin_left, margin_top)
|
||||
.unwrap();
|
||||
|
||||
cairo_context.paint().unwrap();
|
||||
}
|
||||
}
|
||||
122
src/widgets/print_preview.rs
Normal file
122
src/widgets/print_preview.rs
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
//! Print preview widget that shows a fake page with the image on it
|
||||
|
||||
use crate::deps::*;
|
||||
use crate::widgets::LpPrint;
|
||||
|
||||
use adw::subclass::prelude::*;
|
||||
use glib::Properties;
|
||||
use gtk::prelude::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Default, Properties)]
|
||||
#[properties(wrapper_type = super::LpPrintPreview)]
|
||||
pub struct LpPrintPreview {
|
||||
#[property(get, set)]
|
||||
print: OnceCell<LpPrint>,
|
||||
}
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for LpPrintPreview {
|
||||
const NAME: &'static str = "LpPrintPreview";
|
||||
type Type = super::LpPrintPreview;
|
||||
type ParentType = gtk::Widget;
|
||||
}
|
||||
|
||||
impl ObjectImpl for LpPrintPreview {
|
||||
fn constructed(&self) {
|
||||
self.parent_constructed();
|
||||
self.obj().set_halign(gtk::Align::Center);
|
||||
self.obj().set_valign(gtk::Align::Start);
|
||||
self.obj().set_overflow(gtk::Overflow::Hidden);
|
||||
}
|
||||
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
Self::derived_properties()
|
||||
}
|
||||
fn set_property(&self, id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
self.derived_set_property(id, value, pspec)
|
||||
}
|
||||
fn property(&self, id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
self.derived_property(id, pspec)
|
||||
}
|
||||
}
|
||||
|
||||
impl WidgetImpl for LpPrintPreview {
|
||||
fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) {
|
||||
let (width, height) = self.display_paper_dimensions();
|
||||
|
||||
let size = match orientation {
|
||||
gtk::Orientation::Horizontal => width,
|
||||
_ => height,
|
||||
};
|
||||
|
||||
(size, size, -1, -1)
|
||||
}
|
||||
|
||||
fn snapshot(&self, snapshot: >k::Snapshot) {
|
||||
let obj = self.obj();
|
||||
|
||||
snapshot.save();
|
||||
|
||||
let scale = obj.print().user_scale() * self.display_scale();
|
||||
let margin_left = obj.print().user_margin_left() * self.display_scale();
|
||||
let margin_top = obj.print().user_margin_top() * self.display_scale();
|
||||
|
||||
if let Some(texture) = self.obj().print().image().print_data(scale) {
|
||||
let area = graphene::Rect::new(
|
||||
margin_left as f32,
|
||||
margin_top as f32,
|
||||
texture.width() as f32,
|
||||
texture.height() as f32,
|
||||
);
|
||||
|
||||
snapshot.append_texture(&texture, &area);
|
||||
|
||||
// Border around image
|
||||
snapshot.append_inset_shadow(
|
||||
&gsk::RoundedRect::from_rect(area, 0.),
|
||||
&gdk::RGBA::new(0., 0., 0., 0.3),
|
||||
0.,
|
||||
0.,
|
||||
1.,
|
||||
0.,
|
||||
);
|
||||
}
|
||||
snapshot.restore();
|
||||
}
|
||||
}
|
||||
|
||||
impl LpPrintPreview {
|
||||
/// Returns scale factor from print pixel to preview size
|
||||
fn display_scale(&self) -> f64 {
|
||||
let print = self.obj().print();
|
||||
|
||||
let height = print.page_setup().paper_height(gtk::Unit::Inch) * print.dpi();
|
||||
let width = print.page_setup().paper_width(gtk::Unit::Inch) * print.dpi();
|
||||
|
||||
300. / f64::max(width, height)
|
||||
}
|
||||
|
||||
/// Returns total preview size in pixels
|
||||
fn display_paper_dimensions(&self) -> (i32, i32) {
|
||||
let print = self.obj().print();
|
||||
|
||||
let height = print.page_setup().paper_height(gtk::Unit::Inch) * print.dpi();
|
||||
let width = print.page_setup().paper_width(gtk::Unit::Inch) * print.dpi();
|
||||
|
||||
let scale = self.display_scale();
|
||||
|
||||
((width * scale) as i32, (height * scale) as i32)
|
||||
}
|
||||
}
|
||||
}
|
||||
glib::wrapper! {
|
||||
pub struct LpPrintPreview(ObjectSubclass<imp::LpPrintPreview>)
|
||||
@extends gtk::Widget;
|
||||
}
|
||||
|
||||
#[gtk::template_callbacks]
|
||||
impl LpPrintPreview {}
|
||||
|
|
@ -305,11 +305,6 @@ mod imp {
|
|||
self.status_page
|
||||
.set_icon_name(Some(&format!("{}-symbolic", config::APP_ID)));
|
||||
|
||||
// Set help overlay
|
||||
let builder = gtk::Builder::from_resource("/org/gnome/Loupe/gtk/help_overlay.ui");
|
||||
let help_overlay = builder.object("help_overlay").unwrap();
|
||||
obj.set_help_overlay(Some(&help_overlay));
|
||||
|
||||
self.drop_target.set_types(&[gdk::FileList::static_type()]);
|
||||
|
||||
// For callbacks, you will want to reference the GTK docs on
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue