Implement Properties

Implement a properties sidebar. In order to prevent
the UI from freezing, we use futures to load and cancel
loading file information. We also make use of
`gdk_pixbuf` and `svg_metadata` in order to get the file's
metadata before the file fully loads, meaning there's
little to no delay between changing files and seeing
the metadata for large images.

The properties widget uses expressions and
properties to avoid glue code for setting each label,
allowing us to have a clean separation between UI
and data.
This commit is contained in:
Christopher Davis 2022-07-26 11:31:11 -04:00
parent 1e95dd4cbb
commit ba4661031f
12 changed files with 664 additions and 45 deletions

110
data/gtk/properties_view.ui Normal file
View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<interface>
<template class="LpPropertiesView" parent="AdwBin">
<property name="width-request">255</property>
<property name="child">
<object class="AdwPreferencesPage">
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Folder</property>
<property name="activatable-widget">folder_button</property>
<binding name="subtitle">
<closure type="gchararray" function="folder_name">
<lookup name="file">LpPropertiesView</lookup>
</closure>
</binding>
<child type="suffix">
<object class="GtkButton" id="folder_button">
<property name="valign">center</property>
<property name="action-name">properties.open-folder</property>
<property name="icon-name">folder-open-symbolic</property>
<property name="tooltip-text" translatable="yes">Open Containing Folder</property>
<style>
<class name="flat"/>
</style>
</object>
</child>
<style>
<class name="property"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Image Size</property>
<binding name="subtitle">
<lookup name="dimensions">LpPropertiesView</lookup>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">File Type</property>
<binding name="subtitle">
<closure type="gchararray" function="file_type">
<lookup name="file-info">LpPropertiesView</lookup>
</closure>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">File Size</property>
<binding name="subtitle">
<closure type="gchararray" function="file_size">
<lookup name="file-info">LpPropertiesView</lookup>
</closure>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="AdwPreferencesGroup">
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Created</property>
<binding name="subtitle">
<closure type="gchararray" function="created_date">
<lookup name="file-info">LpPropertiesView</lookup>
</closure>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
<child>
<object class="AdwActionRow">
<property name="title" translatable="yes">Modified</property>
<binding name="subtitle">
<closure type="gchararray" function="modified_date">
<lookup name="file-info">LpPropertiesView</lookup>
</closure>
</binding>
<style>
<class name="property"/>
</style>
</object>
</child>
</object>
</child>
</object>
</property>
</template>
</interface>

View file

@ -18,6 +18,12 @@
<property name="swipe-to-close">False</property>
<property name="locked">true</property>
<property name="modal">false</property>
<binding name="fold-policy">
<closure type="AdwFlapFoldPolicy" function="fold_policy">
<lookup name="fullscreened">LpWindow</lookup>
<lookup type="AdwFlap" name="reveal-flap">properties_flap</lookup>
</closure>
</binding>
<property name="flap">
<object class="GtkHeaderBar" id="headerbar">
<property name="show_title_buttons" bind-source="LpWindow" bind-property="fullscreened" bind-flags="sync-create|invert-boolean"/>
@ -35,39 +41,60 @@
<property name="tooltip-text" translatable="yes">Primary Menu</property>
</object>
</child>
<child type="end">
<object class="GtkToggleButton" id="properties_button">
<property name="icon-name">help-about-symbolic</property>
</object>
</child>
</object>
</property>
<property name="content">
<object class="AdwToastOverlay" id="toast_overlay">
<property name="child">
<object class="GtkStack" id="stack">
<property name="visible-child">status_page</property>
<property name="vexpand">True</property>
<child>
<object class="AdwStatusPage" id="status_page">
<property name="title" translatable="yes">Image Viewer</property>
<property name="description" translatable="yes">Click "Open File…" or drag an image here.</property>
<object class="AdwFlap" id="properties_flap">
<property name="flap-position">end</property>
<property name="fold-policy">never</property>
<property name="locked">True</property>
<property name="reveal-flap" bind-source="properties_button" bind-property="active" bind-flags="bidirectional|sync-create"/>
<property name="content">
<object class="GtkStack" id="stack">
<property name="visible-child">status_page</property>
<property name="vexpand">True</property>
<child>
<object class="GtkButton">
<property name="halign">center</property>
<property name="label" translatable="yes">Open File…</property>
<property name="action-name">win.open</property>
<style>
<class name="pill"/>
<class name="suggested-action"/>
</style>
<object class="AdwStatusPage" id="status_page">
<property name="title" translatable="yes">Image Viewer</property>
<property name="description" translatable="yes">Click "Open File…" or drag an image here.</property>
<child>
<object class="GtkButton">
<property name="halign">center</property>
<property name="label" translatable="yes">Open File…</property>
<property name="action-name">win.open</property>
<style>
<class name="pill"/>
<class name="suggested-action"/>
</style>
</object>
</child>
</object>
</child>
<child>
<object class="LpImageView" id="image_view"/>
</child>
<child>
<object class="GtkDropTarget" id="drop_target">
<property name="actions">copy</property>
</object>
</child>
</object>
</child>
<child>
<object class="LpImageView" id="image_view"/>
</child>
<child>
<object class="GtkDropTarget" id="drop_target">
<property name="actions">copy</property>
</property>
<property name="separator">
<object class="GtkSeparator"/>
</property>
<property name="flap">
<object class="LpPropertiesView" id="properties_view">
<property name="file" bind-source="image_view" bind-property="active-file" bind-flags="sync-create"/>
</object>
</child>
</property>
</object>
</property>
</object>

View file

@ -5,6 +5,8 @@
<file compressed="true" preprocess="xml-stripblanks">gtk/window.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/image_page.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/image_view.ui</file>
<file compressed="true" preprocess="xml-stripblanks">gtk/properties_view.ui</file>
<file>style.css</file>
<file>style-hc.css</file>
</gresource>
</gresources>

3
data/style-hc.css Normal file
View file

@ -0,0 +1,3 @@
row.property label.title {
opacity: 0.9;
}

View file

@ -36,4 +36,14 @@ flap.fullscreen-widget dimming, shadow, border, outline {
);
background-size: 20px 20px;
background-position: 0 0, 0 10px, 10px -10px, -10px 0px;
}
}
row.property label.title {
opacity: 0.55;
font-size: smaller;
}
row.property label.subtitle {
opacity: 1.0;
font-size: 11pt;
}