mirror of
https://github.com/johrpan/musicus.git
synced 2025-10-26 19:57:25 +01:00
Add drag and drop to work editor
This commit is contained in:
parent
911d0b070c
commit
0eacd255e8
2 changed files with 65 additions and 22 deletions
|
|
@ -74,6 +74,7 @@ impl WorkEditor {
|
||||||
instrument_frame.set_child(Some(&instrument_list.widget));
|
instrument_frame.set_child(Some(&instrument_list.widget));
|
||||||
|
|
||||||
let part_list = List::new();
|
let part_list = List::new();
|
||||||
|
part_list.set_enable_dnd(true);
|
||||||
structure_frame.set_child(Some(&part_list.widget));
|
structure_frame.set_child(Some(&part_list.widget));
|
||||||
|
|
||||||
let (id, composer, instruments, structure) = match work {
|
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| {
|
this.part_list.set_make_widget_cb(clone!(@strong this => move |index| {
|
||||||
let pos = &this.structure.borrow()[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"));
|
let delete_button = gtk::Button::from_icon_name(Some("user-trash-symbolic"));
|
||||||
delete_button.set_valign(gtk::Align::Center);
|
delete_button.set_valign(gtk::Align::Center);
|
||||||
|
|
||||||
|
|
@ -279,11 +268,9 @@ impl WorkEditor {
|
||||||
let row = libhandy::ActionRow::new();
|
let row = libhandy::ActionRow::new();
|
||||||
row.set_activatable(true);
|
row.set_activatable(true);
|
||||||
row.set_title(Some(&pos.get_title()));
|
row.set_title(Some(&pos.get_title()));
|
||||||
row.add_prefix(&handle);
|
|
||||||
row.add_suffix(&delete_button);
|
row.add_suffix(&delete_button);
|
||||||
row.add_suffix(&edit_button);
|
row.add_suffix(&edit_button);
|
||||||
row.set_activatable_widget(Some(&edit_button));
|
row.set_activatable_widget(Some(&edit_button));
|
||||||
row.add_controller(&drop_target);
|
|
||||||
|
|
||||||
if let PartOrSection::Part(_) = pos {
|
if let PartOrSection::Part(_) = pos {
|
||||||
// TODO: Replace with better solution to differentiate parts and sections.
|
// TODO: Replace with better solution to differentiate parts and sections.
|
||||||
|
|
@ -293,6 +280,16 @@ impl WorkEditor {
|
||||||
row.upcast()
|
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 |_| {
|
add_part_button.connect_clicked(clone!(@strong this => move |_| {
|
||||||
let navigator = this.navigator.borrow().clone();
|
let navigator = this.navigator.borrow().clone();
|
||||||
if let Some(navigator) = navigator {
|
if let Some(navigator) = navigator {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
use super::indexed_list_model::{IndexedListModel, ItemIndex};
|
use super::indexed_list_model::{IndexedListModel, ItemIndex};
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::prelude::*;
|
use gtk::prelude::*;
|
||||||
use std::cell::RefCell;
|
use std::cell::{Cell, RefCell};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
/// A simple list of widgets.
|
/// A simple list of widgets.
|
||||||
|
|
@ -9,8 +9,10 @@ pub struct List {
|
||||||
pub widget: gtk::ListBox,
|
pub widget: gtk::ListBox,
|
||||||
model: IndexedListModel,
|
model: IndexedListModel,
|
||||||
filter: gtk::CustomFilter,
|
filter: gtk::CustomFilter,
|
||||||
|
enable_dnd: Cell<bool>,
|
||||||
make_widget_cb: RefCell<Option<Box<dyn Fn(usize) -> gtk::Widget>>>,
|
make_widget_cb: RefCell<Option<Box<dyn Fn(usize) -> gtk::Widget>>>,
|
||||||
filter_cb: RefCell<Option<Box<dyn Fn(usize) -> bool>>>,
|
filter_cb: RefCell<Option<Box<dyn Fn(usize) -> bool>>>,
|
||||||
|
move_cb: RefCell<Option<Box<dyn Fn(usize, usize)>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl List {
|
impl List {
|
||||||
|
|
@ -31,8 +33,10 @@ impl List {
|
||||||
widget,
|
widget,
|
||||||
model,
|
model,
|
||||||
filter,
|
filter,
|
||||||
|
enable_dnd: Cell::new(false),
|
||||||
make_widget_cb: RefCell::new(None),
|
make_widget_cb: RefCell::new(None),
|
||||||
filter_cb: RefCell::new(None),
|
filter_cb: RefCell::new(None),
|
||||||
|
move_cb: RefCell::new(None),
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filter.set_filter_func(clone!(@strong this => move |index| {
|
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| {
|
this.widget.bind_model(Some(&filter_model), clone!(@strong this => move |index| {
|
||||||
let index = index.downcast_ref::<ItemIndex>().unwrap().get() as usize;
|
let index = index.downcast_ref::<ItemIndex>().unwrap().get() as usize;
|
||||||
if let Some(cb) = &*this.make_widget_cb.borrow() {
|
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 {
|
} 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()
|
gtk::Label::new(None).upcast()
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
@ -56,6 +91,11 @@ impl List {
|
||||||
this
|
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.
|
/// 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) {
|
pub fn set_make_widget_cb<F: Fn(usize) -> gtk::Widget + 'static>(&self, cb: F) {
|
||||||
self.make_widget_cb.replace(Some(Box::new(cb)));
|
self.make_widget_cb.replace(Some(Box::new(cb)));
|
||||||
|
|
@ -67,6 +107,12 @@ impl List {
|
||||||
self.filter_cb.replace(Some(Box::new(cb)));
|
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.
|
/// Select an item by its index. If the index is out of range, nothing will happen.
|
||||||
pub fn select(&self, index: usize) {
|
pub fn select(&self, index: usize) {
|
||||||
let row = self.widget.get_row_at_index(index as i32);
|
let row = self.widget.get_row_at_index(index as i32);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue