diff --git a/include/modules/wlr/taskbar.hpp b/include/modules/wlr/taskbar.hpp index 74b0109..da1c93b 100644 --- a/include/modules/wlr/taskbar.hpp +++ b/include/modules/wlr/taskbar.hpp @@ -74,6 +74,10 @@ class Task { std::string app_id_; uint32_t state_ = 0; + int32_t drag_start_x; + int32_t drag_start_y; + int32_t drag_start_button = -1; + private: std::string repr() const; std::string state_string(bool = false) const; @@ -105,6 +109,11 @@ class Task { /* Callbacks for Gtk events */ bool handle_clicked(GdkEventButton *); + bool handle_button_release(GdkEventButton *); + bool handle_motion_notify(GdkEventMotion *); + void handle_drag_data_get(const Glib::RefPtr& context, Gtk::SelectionData& selection_data, guint info, guint time); + void handle_drag_data_received(const Glib::RefPtr& context, int x, int y, Gtk::SelectionData selection_data, guint info, guint time); + public: bool operator==(const Task &) const; diff --git a/src/modules/wlr/taskbar.cpp b/src/modules/wlr/taskbar.cpp index 156624d..80290bd 100644 --- a/src/modules/wlr/taskbar.cpp +++ b/src/modules/wlr/taskbar.cpp @@ -251,6 +251,10 @@ static const struct zwlr_foreign_toplevel_handle_v1_listener toplevel_handle_imp .parent = tl_handle_parent, }; +static const std::vector target_entries = { + Gtk::TargetEntry("WAYBAR_TOPLEVEL", Gtk::TARGET_SAME_APP, 0) +}; + Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, struct zwlr_foreign_toplevel_handle_v1 *tl_handle, struct wl_seat *seat) : bar_{bar}, @@ -309,9 +313,19 @@ Task::Task(const waybar::Bar &bar, const Json::Value &config, Taskbar *tbar, /* Handle click events if configured */ if (config_["on-click"].isString() || config_["on-click-middle"].isString() || config_["on-click-right"].isString()) { - button_.add_events(Gdk::BUTTON_PRESS_MASK); - button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); } + + button_.add_events(Gdk::BUTTON_PRESS_MASK); + button_.signal_button_press_event().connect(sigc::mem_fun(*this, &Task::handle_clicked), false); + button_.signal_button_release_event().connect(sigc::mem_fun(*this, &Task::handle_button_release), false); + + button_.signal_motion_notify_event().connect(sigc::mem_fun(*this, &Task::handle_motion_notify), false); + + button_.drag_source_set(target_entries, Gdk::BUTTON1_MASK, Gdk::ACTION_MOVE); + button_.drag_dest_set(target_entries, Gtk::DEST_DEFAULT_ALL, Gdk::ACTION_MOVE); + + button_.signal_drag_data_get().connect(sigc::mem_fun(*this, &Task::handle_drag_data_get), false); + button_.signal_drag_data_received().connect(sigc::mem_fun(*this, &Task::handle_drag_data_received), false); } Task::~Task() { @@ -495,6 +509,14 @@ void Task::handle_closed() { } bool Task::handle_clicked(GdkEventButton *bt) { + /* filter out additional events for double/triple clicks */ + if (bt->type == GDK_BUTTON_PRESS) { + /* save where the button press ocurred in case it becomes a drag */ + drag_start_button = bt->button; + drag_start_x = bt->x; + drag_start_y = bt->y; + } + std::string action; if (config_["on-click"].isString() && bt->button == 1) action = config_["on-click"].asString(); @@ -528,6 +550,60 @@ bool Task::handle_clicked(GdkEventButton *bt) { return true; } +bool Task::handle_button_release(GdkEventButton *bt) { + drag_start_button = -1; + return false; +} + +bool Task::handle_motion_notify(GdkEventMotion *mn) { + if (drag_start_button == -1) + return false; + + if ( button_.drag_check_threshold(drag_start_x, drag_start_y, mn->x, mn->y)) { + /* start drag in addition to other assigned action */ + auto target_list = Gtk::TargetList::create(target_entries); + auto refptr = Glib::RefPtr(target_list); + auto drag_context = button_.drag_begin(refptr, Gdk::DragAction::ACTION_MOVE, drag_start_button, (GdkEvent*)mn); + } + + return false; +} + +void Task::handle_drag_data_get(const Glib::RefPtr& context, Gtk::SelectionData& selection_data, guint info, guint time) +{ + spdlog::debug("drag_data_get"); + void* button_addr = (void*)&this->button_; + + selection_data.set( + "WAYBAR_TOPLEVEL", + 32, + (const guchar *)&button_addr, + sizeof (gpointer)); +} + +void Task::handle_drag_data_received(const Glib::RefPtr& context, int x, int y, Gtk::SelectionData selection_data, guint info, guint time) +{ + spdlog::debug("drag_data_received"); + gpointer handle = *(gpointer*)selection_data.get_data(); + auto dragged_button = (Gtk::Button*)handle; + + if(dragged_button == &this->button_) + return; + + auto parent_of_dragged = dragged_button->get_parent(); + auto parent_of_dest = this->button_.get_parent(); + + if(parent_of_dragged != parent_of_dest) + return; + + auto box = (Gtk::Box*)parent_of_dragged; + + auto position_prop = box->child_property_position(this->button_); + auto position = position_prop.get_value(); + + box->reorder_child(*dragged_button, position); +} + bool Task::operator==(const Task &o) const { return o.id_ == id_; } bool Task::operator!=(const Task &o) const { return o.id_ != id_; }