abaddon-ppc/components/draglistbox.cpp
ouwou ae38add931 add ability to reorder roles
- some other small tweaks
2021-03-12 01:55:46 -05:00

142 lines
5.4 KiB
C++

#include "draglistbox.hpp"
DragListBox::DragListBox() {
drag_dest_set(m_entries, Gtk::DEST_DEFAULT_MOTION | Gtk::DEST_DEFAULT_DROP, Gdk::ACTION_MOVE);
}
void DragListBox::row_drag_begin(Gtk::Widget *widget, const Glib::RefPtr<Gdk::DragContext> &context) {
m_drag_row = dynamic_cast<Gtk::ListBoxRow *>(widget->get_ancestor(GTK_TYPE_LIST_BOX_ROW));
auto alloc = m_drag_row->get_allocation();
auto surface = Cairo::ImageSurface::create(Cairo::FORMAT_ARGB32, alloc.get_width(), alloc.get_height());
auto cr = Cairo::Context::create(surface);
m_drag_row->get_style_context()->add_class("drag-icon");
gtk_widget_draw(reinterpret_cast<GtkWidget *>(m_drag_row->gobj()), cr->cobj());
m_drag_row->get_style_context()->remove_class("drag-icon");
int x, y;
widget->translate_coordinates(*m_drag_row, 0, 0, x, y);
surface->set_device_offset(-x, -y);
context->set_icon(surface);
}
bool DragListBox::on_drag_motion(const Glib::RefPtr<Gdk::DragContext> &context, gint x, gint y, guint time) {
if (y > m_hover_top || y < m_hover_bottom) {
auto *row = get_row_at_y(y);
if (row == nullptr) return true;
const bool old_top = m_top;
const auto alloc = row->get_allocation();
const int hover_row_y = alloc.get_y();
const int hover_row_height = alloc.get_height();
if (row != m_drag_row) {
if (y < hover_row_y + hover_row_height / 2) {
m_hover_top = hover_row_y;
m_hover_bottom = m_hover_top + hover_row_height / 2;
row->get_style_context()->add_class("drag-hover-top");
row->get_style_context()->remove_class("drag-hover-bottom");
m_top = true;
} else {
m_hover_top = hover_row_y + hover_row_height / 2;
m_hover_bottom = hover_row_y + hover_row_height;
row->get_style_context()->add_class("drag-hover-bottom");
row->get_style_context()->remove_class("drag-hover-top");
m_top = false;
}
}
if (m_hover_row != nullptr && m_hover_row != row) {
if (old_top)
m_hover_row->get_style_context()->remove_class("drag-hover-top");
else
m_hover_row->get_style_context()->remove_class("drag-hover-bottom");
}
m_hover_row = row;
}
check_scroll(y);
if (m_should_scroll && !m_scrolling) {
m_scrolling = true;
Glib::signal_timeout().connect(sigc::mem_fun(*this, &DragListBox::scroll), SCROLL_DELAY);
}
return true;
}
void DragListBox::on_drag_leave(const Glib::RefPtr<Gdk::DragContext> &context, guint time) {
m_should_scroll = false;
}
void DragListBox::check_scroll(gint y) {
if (!get_focus_vadjustment())
return;
const double vadjustment_min = get_focus_vadjustment()->get_value();
const double vadjustment_max = get_focus_vadjustment()->get_page_size() + vadjustment_min;
const double show_min = std::max(0, y - SCROLL_DISTANCE);
const double show_max = std::min(get_focus_vadjustment()->get_upper(), static_cast<double>(y) + SCROLL_DISTANCE);
if (vadjustment_min > show_min) {
m_should_scroll = true;
m_scroll_up = true;
} else if (vadjustment_max < show_max) {
m_should_scroll = true;
m_scroll_up = false;
} else {
m_should_scroll = false;
}
}
bool DragListBox::scroll() {
if (m_should_scroll) {
if (m_scroll_up) {
get_focus_vadjustment()->set_value(get_focus_vadjustment()->get_value() - SCROLL_STEP_SIZE);
} else {
get_focus_vadjustment()->set_value(get_focus_vadjustment()->get_value() + SCROLL_STEP_SIZE);
}
} else {
m_scrolling = false;
}
return m_should_scroll;
}
void DragListBox::on_drag_data_received(const Glib::RefPtr<Gdk::DragContext> &context, int x, int y, const Gtk::SelectionData &selection_data, guint info, guint time) {
int index = 0;
if (m_hover_row != nullptr) {
if (m_top) {
index = m_hover_row->get_index() - 1;
m_hover_row->get_style_context()->remove_class("drag-hover-top");
} else {
index = m_hover_row->get_index();
m_hover_row->get_style_context()->remove_class("drag-hover-bottom");
}
Gtk::Widget *handle = *reinterpret_cast<Gtk::Widget *const *>(selection_data.get_data());
auto *row = dynamic_cast<Gtk::ListBoxRow *>(handle->get_ancestor(GTK_TYPE_LIST_BOX_ROW));
if (row != nullptr && row != m_hover_row) {
if (row->get_index() > index)
index += 1;
if (m_signal_on_drop.emit(row, index)) return;
row->get_parent()->remove(*row);
insert(*row, index);
}
}
m_drag_row = nullptr;
}
void DragListBox::add_draggable(Gtk::ListBoxRow *widget) {
widget->drag_source_set(m_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE);
widget->signal_drag_begin().connect(sigc::bind<0>(sigc::mem_fun(*this, &DragListBox::row_drag_begin), widget));
widget->signal_drag_data_get().connect([this, widget](const Glib::RefPtr<Gdk::DragContext> &context, Gtk::SelectionData &selection_data, guint info, guint time) {
selection_data.set("GTK_LIST_BOX_ROW", 32, reinterpret_cast<const guint8 *>(&widget), sizeof(&widget));
});
add(*widget);
}
DragListBox::type_signal_on_drop DragListBox::signal_on_drop() {
return m_signal_on_drop;
}