diff --git a/README.md b/README.md index 3324aa1..c5be5fa 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Options: -W, --content-width Min width of the main window [default: 360] -H, --content-height Default height of the main window [default: 360] -I, --from-stdin Accept paths from stdin - -a, --all Drag all the items together + -a, --all Show a drag all button -A, --all-compact Show only the number of items and drag them together -n, --no-click Don't open files on click -b, --basename Always show basename of each file diff --git a/src/list_view.rs b/src/list_view.rs index d90b0d3..69447ac 100644 --- a/src/list_view.rs +++ b/src/list_view.rs @@ -1,11 +1,11 @@ -use glib_macros::clone; -use gtk::gdk::*; +use glib::clone; use gtk::gio::{self, ListStore}; -use gtk::prelude::*; use gtk::{ gdk, CenterBox, DragSource, Label, ListItem, ListView, MultiSelection, SignalListItemFactory, Widget, }; +use gtk::gdk::*; +use gtk::prelude::*; use crate::file_object::FileObject; use crate::util::{ @@ -48,33 +48,25 @@ fn build_list_data() -> (MultiSelection, SignalListItemFactory) { fn create_drag_source(row: &CenterBox, selection: &MultiSelection) -> DragSource { let drag_source = DragSource::new(); - if ARGS.get().unwrap().all { - setup_drag_source_all( - &drag_source, - &selection.model().unwrap().downcast::().unwrap(), - ); - } else { - drag_source.connect_prepare(clone!(@weak row, @weak selection, => @default-return None, move |me, _, _| { - // This will prevent the click to trigger, a drag should happen! - me.set_state(gtk::EventSequenceState::Claimed); - let selected = selection.selection(); - let mut files : Vec = Vec::with_capacity(selected.size() as usize); - - for index in 0..selected.size() { - files.push(selection.item(selected.nth(index as u32)).unwrap().downcast::().unwrap().file().uri().to_string()); - } - - // Is the activated row also selected? - let row_file = get_file(&row).uri().to_string(); - if !files.contains(&row_file) - { - selection.unselect_all(); - generate_content_provider(&[row_file]) - } else { - generate_content_provider(&files) - } - })); - } + drag_source.connect_prepare(clone!(@weak row, @weak selection, => @default-return None, move |me, _, _| { + // This will prevent the click to trigger, a drag should happen! + me.set_state(gtk::EventSequenceState::Claimed); + let selected = selection.selection(); + let mut files : Vec = Vec::with_capacity(selected.size() as usize); + for index in 0..selected.size() { + files.push(selection.item(selected.nth(index as u32)).unwrap().downcast::().unwrap().file().uri().to_string()); + } + + // Is the activated row also selected? + let row_file = get_file(&row).uri().to_string(); + if !files.contains(&row_file) + { + selection.unselect_all(); + generate_content_provider(&[row_file]) + } else { + generate_content_provider(&files) + } + })); if ARGS.get().unwrap().and_exit { drag_source_and_exit(&drag_source); @@ -151,13 +143,17 @@ fn setup_factory(factory: &SignalListItemFactory, list: &MultiSelection) { // show either relative or absolute path // only used for the display label - let str = if ARGS.get().unwrap().basename || file_object - .file() - .has_parent(Some(CURRENT_DIRECTORY.get().unwrap()) - ) { - file_object.file() - .basename().unwrap() - .to_str().unwrap() + let str = if ARGS.get().unwrap().basename + || file_object + .file() + .has_parent(Some(CURRENT_DIRECTORY.get().unwrap())) + { + file_object + .file() + .basename() + .unwrap() + .to_str() + .unwrap() .to_string() } else { path.to_owned() @@ -180,3 +176,43 @@ fn setup_factory(factory: &SignalListItemFactory, list: &MultiSelection) { } }); } + +/// Creates an outer box that adds a drag all button to the top +pub fn create_outer_box(list: &ListWidget) -> gtk::Box { + let outer_box = gtk::Box::new(gtk::Orientation::Vertical, 0); + let row = gtk::CenterBox::builder() + .height_request(ARGS.get().unwrap().icon_size) + .focusable(true) + .build(); + let label = Label::builder() + .label("Drag All Items") + .css_classes(["drag"]) + .hexpand(true) + .tooltip_text("Drag All Items") + .ellipsize(gtk::pango::EllipsizeMode::End); + row.set_center_widget(Some(&label.build())); + + let drag_source = DragSource::new(); + setup_drag_source_all(&drag_source, &list.list_model); + if !ARGS.get().unwrap().no_click { + let gesture_click = create_gesture_click(&row); + row.add_controller(gesture_click); + } + if ARGS.get().unwrap().and_exit { + drag_source_and_exit(&drag_source); + } + + // Add styling + let provider = gtk::CssProvider::new(); + provider.load_from_data(include_str!("style.css")); + gtk::style_context_add_provider_for_display( + >k::gdk::Display::default().expect("Could not connect to a display."), + &provider, + gtk::STYLE_PROVIDER_PRIORITY_APPLICATION, + ); + + row.add_controller(drag_source); + outer_box.append(&row); + outer_box.append(&list.widget); + outer_box +} diff --git a/src/main.rs b/src/main.rs index 44bfeda..e7a925a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,9 +7,9 @@ use compact_view::generate_compact_view; use file_object::FileObject; use gtk::gio::{ApplicationFlags, ListStore}; use gtk::glib::{self, clone, set_program_name, Continue, MainContext, Priority}; -use gtk::prelude::*; use gtk::{gio, Application, ApplicationWindow, EventControllerKey, PolicyType, ScrolledWindow}; -use list_view::generate_list_view; +use gtk::prelude::*; +use list_view::{create_outer_box, generate_list_view}; mod compact_view; mod file_object; @@ -63,7 +63,7 @@ struct Cli { #[arg(short = 'I', long)] from_stdin: bool, - /// Drag all the items together + /// Show a drag all button #[arg(short = 'a', long)] all: bool, @@ -115,17 +115,27 @@ fn build_ui(app: &Application) { ); } // Create a scrollable list - let list_data = if ARGS.get().unwrap().all_compact { - generate_compact_view() + let (outer_box, list_data) = if ARGS.get().unwrap().all_compact { + // Create compact list + (None, generate_compact_view()) + } else if ARGS.get().unwrap().all { + // Create regular list with a drag all button + let list = generate_list_view(); + (Some(create_outer_box(&list).upcast::()), list) } else { - generate_list_view() + // Create regular list + (None, generate_list_view()) }; let scrolled_window = ScrolledWindow::builder() .hscrollbar_policy(PolicyType::Never) // Disable horizontal scrolling .vexpand(true) .hexpand(true) - .child(&list_data.widget) + .child(if let Some(outer_box) = &outer_box { + outer_box + } else { + &list_data.widget + }) .build(); let titlebar = gtk::HeaderBar::builder() diff --git a/src/util.rs b/src/util.rs index 8972d9b..c0951d9 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,8 +1,8 @@ use gtk::gdk::{ContentProvider, DragAction, FileList}; use gtk::gio::{self, File, ListStore}; use gtk::glib::{clone, Bytes}; -use gtk::prelude::*; use gtk::{gdk, glib, DragSource, DropTarget, EventSequenceState, Widget}; +use gtk::prelude::*; use crate::file_object::FileObject; use crate::ARGS; @@ -31,13 +31,13 @@ pub fn generate_content_provider<'a>( paths: impl IntoIterator, ) -> Option { let mut uri_list = paths - .into_iter() - .cloned() - .collect::>() - .join("\r\n"); - + .into_iter() + .cloned() + .collect::>() + .join("\r\n"); + if uri_list.is_empty() { - return None + return None; } else { uri_list += "\r\n"; let bytes = Bytes::from(uri_list.as_bytes());