window: Implement drag and drop

The main page is no longer a lie.
This commit is contained in:
Christopher Davis 2021-12-27 15:03:31 -08:00
parent 5d9567507c
commit be70bdafe5
5 changed files with 67 additions and 11 deletions

View file

@ -20,6 +20,7 @@ features = ["feature_gtk4"]
[dependencies.gtk] [dependencies.gtk]
package = "gtk4" package = "gtk4"
git = "https://github.com/gtk-rs/gtk4-rs" git = "https://github.com/gtk-rs/gtk4-rs"
features = ["v4_6"]
[dependencies.adw] [dependencies.adw]
package = "libadwaita" package = "libadwaita"

View file

@ -61,6 +61,11 @@
<child> <child>
<object class="LpImageView" id="image_view"/> <object class="LpImageView" id="image_view"/>
</child> </child>
<child>
<object class="GtkDropTarget" id="drop_target">
<property name="actions">copy</property>
</object>
</child>
</object> </object>
</property> </property>
<style> <style>

View file

@ -81,7 +81,7 @@ mod imp {
fn open(&self, application: &Self::Type, files: &[gio::File], _hint: &str) { fn open(&self, application: &Self::Type, files: &[gio::File], _hint: &str) {
for file in files { for file in files {
let win = LpWindow::new(application); let win = LpWindow::new(application);
win.set_image_from_file(file); win.set_image_from_file(file, true);
win.show(); win.show();
} }
} }

View file

@ -13,13 +13,24 @@ pub fn utf8_collate_key_for_filename(filename: &str) -> String {
} }
pub fn get_file_display_name(file: &gio::File) -> Option<String> { pub fn get_file_display_name(file: &gio::File) -> Option<String> {
let info = file let info = query_attributes(file, vec![&gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME]).ok()?;
.query_info(
*gio::FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
gio::FileQueryInfoFlags::empty(),
gio::Cancellable::NONE,
)
.ok()?;
Some(info.display_name().to_string()) Some(info.display_name().to_string())
} }
pub fn query_attributes(
file: &gio::File,
attributes: Vec<&str>,
) -> Result<gio::FileInfo, glib::Error> {
let mut attributes = attributes;
let mut attr_str = String::from(attributes.remove(0));
for attr in attributes {
attr_str.push_str(&format!(",{}", attr));
}
file.query_info(
&attr_str,
gio::FileQueryInfoFlags::empty(),
gio::Cancellable::NONE,
)
}

View file

@ -26,6 +26,7 @@ use gtk::subclass::prelude::*;
use gtk::CompositeTemplate; use gtk::CompositeTemplate;
use crate::config; use crate::config;
use crate::util;
use crate::widgets::LpImageView; use crate::widgets::LpImageView;
mod imp { mod imp {
@ -65,6 +66,8 @@ mod imp {
pub status_page: TemplateChild<adw::StatusPage>, pub status_page: TemplateChild<adw::StatusPage>,
#[template_child] #[template_child]
pub image_view: TemplateChild<LpImageView>, pub image_view: TemplateChild<LpImageView>,
#[template_child]
pub drop_target: TemplateChild<gtk::DropTarget>,
} }
#[glib::object_subclass] #[glib::object_subclass]
@ -135,6 +138,39 @@ mod imp {
} }
}), }),
); );
self.drop_target.set_types(&[gdk::FileList::static_type()]);
self.drop_target.connect_drop(
clone!(@weak obj => @default-return false, move |_, value, _, _| {
// Here we use a GValue, which is a dynamic object that can hold different types,
// e.g. strings, numbers, or in this case objects. In order to get the GdkFileList
// from the GValue, we need to use the `get()` method.
//
// We've added type annotations here, and written it as `let list: gdk::FileList = ...`,
// but you might also see places where type arguments are used.
// This line could have been written as `let list = value.get::<gdk::FileList>().unwrap()`.
let list: gdk::FileList = value.get().unwrap();
// TODO: Handle this like EOG and make a "directory" out of the given files
let file = list.files().get(0).unwrap().clone();
let info = util::query_attributes(
&file,
vec![&gio::FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE])
.expect("Could not query file info");
if info
.content_type()
.map(|t| t.to_string())
.filter(|t| t.starts_with("image/"))
.is_some() {
obj.set_image_from_file(&file, false);
} else {
log::debug!("{} was not a valid image.", file.uri().to_string());
}
true
}),
);
} }
} }
@ -201,7 +237,7 @@ impl LpWindow {
clone!(@weak self as win, @strong chooser => move |_, resp| { clone!(@weak self as win, @strong chooser => move |_, resp| {
if resp == gtk::ResponseType::Accept { if resp == gtk::ResponseType::Accept {
if let Some(file) = chooser.file() { if let Some(file) = chooser.file() {
win.set_image_from_file(&file); win.set_image_from_file(&file, true);
} }
} }
}), }),
@ -239,13 +275,16 @@ impl LpWindow {
} }
} }
pub fn set_image_from_file(&self, file: &gio::File) { pub fn set_image_from_file(&self, file: &gio::File, resize: bool) {
let imp = imp::LpWindow::from_instance(self); let imp = imp::LpWindow::from_instance(self);
log::debug!("Loading file: {}", file.uri().to_string()); log::debug!("Loading file: {}", file.uri().to_string());
match imp.image_view.set_image_from_file(file) { match imp.image_view.set_image_from_file(file) {
Ok((width, height)) => { Ok((width, height)) => {
if resize {
self.resize_from_dimensions(width, height); self.resize_from_dimensions(width, height);
}
imp.stack.set_visible_child(&*imp.image_view); imp.stack.set_visible_child(&*imp.image_view);
imp.image_view.grab_focus(); imp.image_view.grab_focus();
self.set_actions_enabled(true) self.set_actions_enabled(true)