diff options
Diffstat (limited to 'libmdock/mdock-widget.c')
-rw-r--r-- | libmdock/mdock-widget.c | 189 |
1 files changed, 186 insertions, 3 deletions
diff --git a/libmdock/mdock-widget.c b/libmdock/mdock-widget.c index af3b165..004a590 100644 --- a/libmdock/mdock-widget.c +++ b/libmdock/mdock-widget.c @@ -28,6 +28,9 @@ #include "mdock-item.h" #include "matcher.h" +#define POPUP_SHOW_TIMEOUT 200 +#define POPUP_HIDE_TIMEOUT 500 + struct _MDockWidgetPrivate { gchar *settings_path; @@ -37,6 +40,9 @@ struct _MDockWidgetPrivate GHashTable *appid_to_item; GHashTable *desktopid_to_item; GHashTable *window_to_item; + MDockItem *current_pointed_item; + MDockItem *current_popup_item; + guint popup_timer; gboolean loading_settings : 1; gboolean just_dropped : 1; }; @@ -67,6 +73,12 @@ static const GtkTargetEntry drag_types[] = { "text/uri-list", 0, DRAG_TYPE_URILIST }; +static inline MDockItemWindowSelector * selector_from_item(MDockItem *item) +{ + return g_object_get_qdata((GObject*)item, + mdock_widget_item_window_selector_quark()); +} + static void save_items_to_settings(MDockWidget *self) { GSettings *settings = self->priv->settings; @@ -101,6 +113,77 @@ static void move_item_to_position(MDockWidget *self, GSequenceIter *iter, GSeque g_sequence_move(iter, position); } +static void hide_item_popup(MDockWidget *self) +{ + g_return_if_fail(self->priv->current_popup_item); + g_debug("hiding popup %p", self->priv->current_popup_item); + + MDockItemWindowSelector *selector = + MDOCK_ITEM_WINDOW_SELECTOR(g_object_get_qdata(G_OBJECT(self->priv->current_popup_item), + mdock_widget_item_window_selector_quark())); + + gtk_widget_hide(GTK_WIDGET(selector)); + self->priv->current_popup_item = NULL; +} + +static void show_item_popup(MDockWidget *self, MDockItem *item) +{ + g_warn_if_fail(!self->priv->current_popup_item); + g_debug("showing popup %p", item); + + MDockItemWindowSelector *selector = + MDOCK_ITEM_WINDOW_SELECTOR(g_object_get_qdata(G_OBJECT(item), + mdock_widget_item_window_selector_quark())); + + // Position the popup + GtkOrientation orientation = gtk_orientable_get_orientation(GTK_ORIENTABLE(self)); + GdkWindow *item_window = gtk_widget_get_window(GTK_WIDGET(item)); + GdkScreen *screen = gdk_window_get_screen(item_window); + gint monitor_num = gdk_screen_get_monitor_at_window(screen, item_window); + GdkRectangle monitor; + GtkRequisition requisition; + gint item_x, item_y, item_w, item_h; + gint x, y; + + gdk_window_get_origin(item_window, &item_x, &item_y); + gdk_window_get_size(item_window, &item_w, &item_h); + gdk_screen_get_monitor_geometry(screen, monitor_num, &monitor); + gtk_widget_size_request(GTK_WIDGET(selector), &requisition); + + if (requisition.width > 0 || requisition.height > 0) { + if (orientation == GTK_ORIENTATION_HORIZONTAL) { + x = item_x + item_w / 2 - requisition.width / 2; + if (item_y > (monitor.y + monitor.height / 2)) { + y = item_y - requisition.height; + } else { + y = item_y + item_h; + } + } else { + y = item_y + item_y / 2 - requisition.height / 2; + if (item_x > (monitor.x + monitor.width / 2)) { + x = item_x - requisition.width; + } else { + x = item_x + item_w; + } + } + + if (x + requisition.width > monitor.x + monitor.width) { + x -= x - (monitor.x + monitor.width) + requisition.width; + } else if (x < monitor.x) { + x = monitor.x; + } + + if (y + requisition.height > monitor.y + monitor.height) { + y -= y - (monitor.y + monitor.height) + requisition.height; + } + + gtk_window_move(GTK_WINDOW(selector), x, y); + gtk_widget_show(GTK_WIDGET(selector)); + } + + self->priv->current_popup_item = item; +} + static void handle_item_pinned_changed(MDockWidget *self, GParamSpec *spec, MDockItem *item) { if (!self->priv->loading_settings) { @@ -112,6 +195,14 @@ static gboolean handle_item_button_press(MDockWidget *self, GdkEventButton *even { switch (event->button) { case 3: + if (self->priv->popup_timer) { + g_source_remove(self->priv->popup_timer); + self->priv->popup_timer = 0; + } + if (self->priv->current_popup_item) { + hide_item_popup(self); + } + gtk_menu_popup(GTK_MENU(g_object_get_qdata(G_OBJECT(item), mdock_widget_item_menu_quark())), NULL, NULL, NULL, NULL, event->button, event->time); @@ -303,9 +394,75 @@ static gboolean mdock_widget_drag_motion(GtkWidget *widget, GdkDragContext *cont return TRUE; } +static gboolean handle_popup_timer(gpointer user_data) +{ + MDockWidget *self = MDOCK_WIDGET(user_data); + if (self->priv->current_popup_item) { + hide_item_popup(self); + } else if (self->priv->current_pointed_item) { + show_item_popup(self, self->priv->current_pointed_item); + } + + self->priv->popup_timer = 0; + return G_SOURCE_REMOVE; +} + +static gboolean handle_item_enter(MDockWidget *self, GdkEventCrossing *event, MDockItem *item) +{ + g_debug("Item enter"); + self->priv->current_pointed_item = item; + if (self->priv->current_popup_item) { + if (self->priv->popup_timer) { + g_source_remove(self->priv->popup_timer); + self->priv->popup_timer = 0; + } + hide_item_popup(self); + show_item_popup(self, item); + + } else if (!self->priv->popup_timer) { + self->priv->popup_timer = g_timeout_add(POPUP_SHOW_TIMEOUT, handle_popup_timer, self); + } + return FALSE; +} + +static gboolean handle_item_leave(MDockWidget *self, GdkEventCrossing *event, MDockItem *item) +{ + g_debug("Item leave"); + g_warn_if_fail(self->priv->current_pointed_item == item); + g_warn_if_fail(!self->priv->current_popup_item || self->priv->current_pointed_item == item); + self->priv->current_pointed_item = NULL; + if (self->priv->current_popup_item && !self->priv->popup_timer) { + self->priv->popup_timer = g_timeout_add(POPUP_HIDE_TIMEOUT, handle_popup_timer, self); + } + return FALSE; +} + +static gboolean handle_item_selector_enter(MDockWidget *self, GdkEventCrossing *event, MDockItemWindowSelector *selector) +{ + g_debug("Item selector enter %d", event->detail); + if (event->detail != GDK_NOTIFY_NONLINEAR) return FALSE; + g_warn_if_fail(self->priv->current_popup_item); + if (self->priv->popup_timer) { + g_source_remove(self->priv->popup_timer); + self->priv->popup_timer = 0; + } + return FALSE; +} + +static gboolean handle_item_selector_leave(MDockWidget *self, GdkEventCrossing *event, MDockItemWindowSelector *selector) +{ + g_debug("Item selector leave %d", event->detail); + if (event->detail != GDK_NOTIFY_NONLINEAR) return FALSE; + g_warn_if_fail(self->priv->current_popup_item); + if (!self->priv->popup_timer) { + self->priv->popup_timer = g_timeout_add(POPUP_HIDE_TIMEOUT, handle_popup_timer, self); + } + return FALSE; +} + static void connect_item(MDockWidget *self, MDockItem *item, GSequenceIter *position) { - g_object_set_qdata(G_OBJECT(item), mdock_widget_item_iter_quark(), position); + g_object_set_qdata(G_OBJECT(item), mdock_widget_item_iter_quark(), position);; g_signal_connect_object(item, "notify::pinned", G_CALLBACK(handle_item_pinned_changed), self, @@ -336,12 +493,30 @@ static void connect_item(MDockWidget *self, MDockItem *item, GSequenceIter *posi g_signal_connect_object(item, "drag-data-delete", G_CALLBACK(handle_item_drag_data_delete), self, G_CONNECT_SWAPPED); + g_signal_connect_object(item, "enter-notify-event", + G_CALLBACK(handle_item_enter), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(item, "leave-notify-event", + G_CALLBACK(handle_item_leave), self, + G_CONNECT_SWAPPED); + MDockItemMenu *menu = mdock_item_menu_new(item); g_object_set_qdata_full(G_OBJECT(item), mdock_widget_item_menu_quark(), - mdock_item_menu_new(item), (GDestroyNotify)gtk_widget_destroy); + menu, (GDestroyNotify)gtk_widget_destroy); + MDockItemWindowSelector *selector = mdock_item_window_selector_new(item); g_object_set_qdata_full(G_OBJECT(item), mdock_widget_item_window_selector_quark(), - mdock_item_window_selector_new(item), (GDestroyNotify)gtk_widget_destroy); + selector, (GDestroyNotify)gtk_widget_destroy); + + g_object_bind_property(self, "orientation", + selector, "orientation", + G_BINDING_DEFAULT); + g_signal_connect_object(selector, "enter-notify-event", + G_CALLBACK(handle_item_selector_enter), self, + G_CONNECT_SWAPPED); + g_signal_connect_object(selector, "leave-notify-event", + G_CALLBACK(handle_item_selector_leave), self, + G_CONNECT_SWAPPED); } @@ -403,6 +578,7 @@ static void handle_active_window_changed(MDockWidget *self, WnckWindow *previous if (iter) { MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_set_last_active_window(item, window); + mdock_item_window_selector_set_active_window(selector_from_item(item), window); } } @@ -421,6 +597,7 @@ static void handle_window_closed(MDockWidget *self, WnckWindow *window, WnckScre MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_remove_window(item, window); + mdock_item_window_selector_remove_window(selector_from_item(item), window); g_hash_table_remove(self->priv->window_to_item, window); if (!mdock_item_has_windows(item) && !mdock_item_get_pinned(item)) { @@ -428,6 +605,9 @@ static void handle_window_closed(MDockWidget *self, WnckWindow *window, WnckScre g_hash_table_foreach_remove(self->priv->appid_to_item, filter_deleted_group, iter); g_hash_table_foreach_remove(self->priv->desktopid_to_item, filter_deleted_group, iter); g_sequence_remove(iter); + if (self->priv->current_popup_item == item) { + hide_item_popup(self); + } gtk_widget_destroy(GTK_WIDGET(item)); } } @@ -443,6 +623,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre if (iter) { MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_add_window(item, window); + mdock_item_window_selector_add_window(selector_from_item(item), window); app_id_destroy(appid); g_hash_table_insert(self->priv->window_to_item, window, iter); return; @@ -454,6 +635,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre if (iter) { MDockItem *item = MDOCK_ITEM(g_sequence_get(iter)); mdock_item_add_window(item, window); + mdock_item_window_selector_add_window(selector_from_item(item), window); g_hash_table_insert(self->priv->appid_to_item, appid, iter); // takes ownership of appid g_hash_table_insert(self->priv->window_to_item, window, iter); return; @@ -471,6 +653,7 @@ static void handle_window_opened(MDockWidget *self, WnckWindow *window, WnckScre connect_item(self, item, iter); mdock_item_add_window(item, window); + mdock_item_window_selector_add_window(selector_from_item(item), window); g_hash_table_insert(self->priv->appid_to_item, appid, iter); // takes ownership of appid if (desktopid) { |