Add drag and drop to work editor

This commit is contained in:
Elias Projahn 2021-01-25 16:26:44 +01:00
parent 911d0b070c
commit 0eacd255e8
2 changed files with 65 additions and 22 deletions

View file

@ -74,6 +74,7 @@ impl WorkEditor {
instrument_frame.set_child(Some(&instrument_list.widget));
let part_list = List::new();
part_list.set_enable_dnd(true);
structure_frame.set_child(Some(&part_list.widget));
let (id, composer, instruments, structure) = match work {
@ -208,18 +209,6 @@ impl WorkEditor {
this.part_list.set_make_widget_cb(clone!(@strong this => move |index| {
let pos = &this.structure.borrow()[index];
let drag_source = gtk::DragSource::new();
drag_source.set_content(Some(&gdk::ContentProvider::new_for_value(&(index as u32).to_value())));
let drop_target = gtk::DropTarget::new(glib::Type::U32, gdk::DragAction::MOVE);
drop_target.connect_property_value_notify(clone!(@strong this => move |drop_target| {
println!("{:?} -> {:?}", drop_target.get_value(), index);
}));
let handle = gtk::Image::from_icon_name(Some("open-menu-symbolic"));
handle.add_controller(&drag_source);
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
delete_button.set_valign(gtk::Align::Center);
@ -279,11 +268,9 @@ impl WorkEditor {
let row = libhandy::ActionRow::new();
row.set_activatable(true);
row.set_title(Some(&pos.get_title()));
row.add_prefix(&handle);
row.add_suffix(&delete_button);
row.add_suffix(&edit_button);
row.set_activatable_widget(Some(&edit_button));
row.add_controller(&drop_target);
if let PartOrSection::Part(_) = pos {
// TODO: Replace with better solution to differentiate parts and sections.
@ -293,6 +280,16 @@ impl WorkEditor {
row.upcast()
}));
this.part_list.set_move_cb(clone!(@strong this => move |old_index, new_index| {
let length = {
let mut structure = this.structure.borrow_mut();
structure.swap(old_index, new_index);
structure.len()
};
this.part_list.update(length);
}));
add_part_button.connect_clicked(clone!(@strong this => move |_| {
let navigator = this.navigator.borrow().clone();
if let Some(navigator) = navigator {

View file

@ -1,7 +1,7 @@
use super::indexed_list_model::{IndexedListModel, ItemIndex};
use glib::clone;
use gtk::prelude::*;
use std::cell::RefCell;
use std::cell::{Cell, RefCell};
use std::rc::Rc;
/// A simple list of widgets.
@ -9,8 +9,10 @@ pub struct List {
pub widget: gtk::ListBox,
model: IndexedListModel,
filter: gtk::CustomFilter,
enable_dnd: Cell<bool>,
make_widget_cb: RefCell<Option<Box<dyn Fn(usize) -> gtk::Widget>>>,
filter_cb: RefCell<Option<Box<dyn Fn(usize) -> bool>>>,
move_cb: RefCell<Option<Box<dyn Fn(usize, usize)>>>,
}
impl List {
@ -31,8 +33,10 @@ impl List {
widget,
model,
filter,
enable_dnd: Cell::new(false),
make_widget_cb: RefCell::new(None),
filter_cb: RefCell::new(None),
move_cb: RefCell::new(None),
});
this.filter.set_filter_func(clone!(@strong this => move |index| {
@ -47,8 +51,39 @@ impl List {
this.widget.bind_model(Some(&filter_model), clone!(@strong this => move |index| {
let index = index.downcast_ref::<ItemIndex>().unwrap().get() as usize;
if let Some(cb) = &*this.make_widget_cb.borrow() {
cb(index)
let widget = cb(index);
if this.enable_dnd.get() {
let drag_source = gtk::DragSource::new();
drag_source.connect_drag_begin(clone!(@strong widget => move |_, drag| {
// TODO: Replace with a better solution.
let paintable = gtk::WidgetPaintable::new(Some(&widget));
gtk::DragIcon::set_from_paintable(drag, &paintable, 0, 0);
}));
let drag_value = (index as u32).to_value();
drag_source.set_content(Some(&gdk::ContentProvider::new_for_value(&drag_value)));
let drop_target = gtk::DropTarget::new(glib::Type::U32, gdk::DragAction::COPY);
drop_target.connect_drop(clone!(@strong this => move |_, value, _, _| {
if let Some(cb) = &*this.move_cb.borrow() {
let old_index: u32 = value.get_some().unwrap();
cb(old_index as usize, index);
true
} else {
false
}
}));
widget.add_controller(&drag_source);
widget.add_controller(&drop_target);
}
widget
} else {
// This shouldn't be reachable under normal circumstances.
gtk::Label::new(None).upcast()
}
}));
@ -56,6 +91,11 @@ impl List {
this
}
/// Whether the list should support drag and drop.
pub fn set_enable_dnd(&self, enable: bool) {
self.enable_dnd.set(enable);
}
/// Set the closure to be called to construct widgets for the items.
pub fn set_make_widget_cb<F: Fn(usize) -> gtk::Widget + 'static>(&self, cb: F) {
self.make_widget_cb.replace(Some(Box::new(cb)));
@ -67,6 +107,12 @@ impl List {
self.filter_cb.replace(Some(Box::new(cb)));
}
/// Set the closure to be called to when the use has dragged an item to a
/// new position.
pub fn set_move_cb<F: Fn(usize, usize) + 'static>(&self, cb: F) {
self.move_cb.replace(Some(Box::new(cb)));
}
/// Select an item by its index. If the index is out of range, nothing will happen.
pub fn select(&self, index: usize) {
let row = self.widget.get_row_at_index(index as i32);