From 8de6cb9023955fbc1034062aa2f46bdd1598ac8b Mon Sep 17 00:00:00 2001 From: Robert Carr Date: Fri, 2 Sep 2011 14:54:40 -0400 Subject: Implement an IdoOffscreenProxy object to work around GtkRange/Scale needing grabs, change IdoScaleMenuItem to make use of this. Fixes lp: #804009 --- src/Makefile.am | 7 +- src/idooffscreenproxy.c | 473 ++++++++++++++++++++++++++++++++++++++++++++++++ src/idooffscreenproxy.h | 37 ++++ src/idoscalemenuitem.c | 32 ++-- 4 files changed, 535 insertions(+), 14 deletions(-) create mode 100644 src/idooffscreenproxy.c create mode 100644 src/idooffscreenproxy.h diff --git a/src/Makefile.am b/src/Makefile.am index 5ac5a01..c61474f 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,9 +18,10 @@ sources_h = \ idoentrymenuitem.h \ idomessagedialog.h \ idorange.h \ + idooffscreenproxy.h \ idoscalemenuitem.h \ idotimeline.h \ - libido.h + libido.h EXTRA_DIST = \ ido.list \ @@ -62,7 +63,8 @@ libido_0_1_la_SOURCES = \ idomessagedialog.c \ idorange.c \ idoscalemenuitem.c \ - idotimeline.c + idotimeline.c \ + idooffscreenproxy.c libido3_0_1_la_SOURCES = $(libido_0_1_la_SOURCES) libidoincludedir=$(includedir)/libido$(VER)-0.1/libido @@ -74,6 +76,7 @@ libidoinclude_HEADERS = \ idorange.h \ idoscalemenuitem.h \ idotimeline.h \ + idooffscreenproxy.h \ libido.h libido_0_1_la_LIBADD = $(GTK_LIBS) diff --git a/src/idooffscreenproxy.c b/src/idooffscreenproxy.c new file mode 100644 index 0000000..d8714fa --- /dev/null +++ b/src/idooffscreenproxy.c @@ -0,0 +1,473 @@ +#include +#include +#include "idooffscreenproxy.h" + +struct _IdoOffscreenProxyPrivate +{ + GtkWidget *child; + + GdkWindow *offscreen_window; +}; + +static void ido_offscreen_proxy_realize (GtkWidget *widget); +static void ido_offscreen_proxy_unrealize (GtkWidget *widget); +static void ido_offscreen_proxy_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural); +static void ido_offscreen_proxy_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural); + +static void ido_offscreen_proxy_size_allocate (GtkWidget *widget, + GtkAllocation *allocation); +static gboolean ido_offscreen_proxy_damage (GtkWidget *widget, + GdkEventExpose *event); +static gboolean ido_offscreen_proxy_draw (GtkWidget *widget, + cairo_t *cr); +static void ido_offscreen_proxy_add (GtkContainer *container, + GtkWidget *child); +static void ido_offscreen_proxy_remove (GtkContainer *container, + GtkWidget *widget); +static void ido_offscreen_proxy_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data); +static GType ido_offscreen_proxy_child_type (GtkContainer *container); + +static cairo_surface_t * ido_offscreen_proxy_create_alpha_image_surface (GdkWindow *offscreen, gint width, gint height); + + + +G_DEFINE_TYPE (IdoOffscreenProxy, ido_offscreen_proxy, GTK_TYPE_CONTAINER); + +#define IDO_OFFSCREEN_PROXY_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), IDO_TYPE_OFFSCREEN_PROXY, IdoOffscreenProxyPrivate)) + +static void +ido_offscreen_proxy_class_init (IdoOffscreenProxyClass *klass) +{ + GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); + GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass); + + g_type_class_add_private (klass, sizeof (IdoOffscreenProxyPrivate)); + + widget_class->realize = ido_offscreen_proxy_realize; + widget_class->unrealize = ido_offscreen_proxy_unrealize; + widget_class->get_preferred_width = ido_offscreen_proxy_get_preferred_width; + widget_class->get_preferred_height = ido_offscreen_proxy_get_preferred_height; + widget_class->size_allocate = ido_offscreen_proxy_size_allocate; + widget_class->draw = ido_offscreen_proxy_draw; + + g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET), + IDO_TYPE_OFFSCREEN_PROXY, + g_cclosure_new (G_CALLBACK (ido_offscreen_proxy_damage), + NULL, NULL)); + + container_class->add = ido_offscreen_proxy_add; + container_class->remove = ido_offscreen_proxy_remove; + container_class->forall = ido_offscreen_proxy_forall; + container_class->child_type = ido_offscreen_proxy_child_type; + +} + +static void +ido_offscreen_proxy_init (IdoOffscreenProxy *proxy) +{ + proxy->priv = IDO_OFFSCREEN_PROXY_GET_PRIVATE (proxy); + + gtk_widget_set_has_window (GTK_WIDGET (proxy), TRUE); + + gtk_widget_set_events (GTK_WIDGET(proxy), gtk_widget_get_events (GTK_WIDGET(proxy)) + | GDK_EXPOSURE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_SCROLL_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK); +} + +GtkWidget * +ido_offscreen_proxy_new (void) +{ + return g_object_new (IDO_TYPE_OFFSCREEN_PROXY, NULL); +} + +static GdkWindow * +pick_offscreen_child (GdkWindow *offscreen_window, + double widget_x, double widget_y, + IdoOffscreenProxy *proxy) +{ + GtkAllocation child_area; + + if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child)) + { + gtk_widget_get_allocation (proxy->priv->child, &child_area); + + // if (widget_x >= 0 && widget_x < child_area.width && + // widget_y >= 0 && widget_y < child_area.height) + //return proxy->priv->offscreen_window; + exit(0); + return proxy->priv->offscreen_window; + } + + return NULL; +} + +static void +offscreen_to_parent (GdkWindow *offscreen_window, + double offscreen_x, + double offscreen_y, + double *parent_x, + double *parent_y, + gpointer user_data) +{ + *parent_x = offscreen_x; + *parent_y = offscreen_y; +} + +static void +offscreen_from_parent (GdkWindow *window, + double parent_x, + double parent_y, + double *offscreen_x, + double *offscreen_y, + gpointer user_data) +{ + *offscreen_x = parent_x; + *offscreen_y = parent_y; +} + +static cairo_surface_t * +ido_offscreen_proxy_create_alpha_image_surface (GdkWindow *offscreen, gint width, gint height) +{ + return cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); +} + +static void +ido_offscreen_proxy_realize (GtkWidget *widget) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget); + GtkAllocation allocation, child_area; + GtkStyleContext *context; + GdkWindow *window; + GdkWindowAttr attributes; + gint attributes_mask; + // GtkRequisition child_requisition; + + gtk_widget_set_realized (widget, TRUE); + + gtk_widget_get_allocation (widget, &allocation); + + attributes.x = allocation.x; + attributes.y = allocation.y; + attributes.width = allocation.width; + attributes.height = allocation.height; + + attributes.window_type = GDK_WINDOW_CHILD; + attributes.event_mask = gtk_widget_get_events (widget) + | GDK_EXPOSURE_MASK + | GDK_POINTER_MOTION_MASK + | GDK_BUTTON_PRESS_MASK + | GDK_BUTTON_RELEASE_MASK + | GDK_SCROLL_MASK + | GDK_ENTER_NOTIFY_MASK + | GDK_LEAVE_NOTIFY_MASK; + attributes.visual = gdk_screen_get_rgba_visual (gdk_screen_get_default ());//gtk_widget_get_visual (widget); + attributes.wclass = GDK_INPUT_OUTPUT; + + attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; + + window = gdk_window_new (gtk_widget_get_parent_window (widget), + &attributes, attributes_mask); + + gtk_widget_set_window (widget, window); + gdk_window_set_user_data (window, widget); + + g_signal_connect (window, "pick-embedded-child", + G_CALLBACK (pick_offscreen_child), proxy); + + attributes.window_type = GDK_WINDOW_OFFSCREEN; + attributes.x = attributes.y = 0; + + if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child)) + { + gtk_widget_get_allocation (proxy->priv->child, &child_area); + attributes.width = child_area.width; + attributes.height = child_area.height; + } + + proxy->priv->offscreen_window = gdk_window_new (gtk_widget_get_root_window (widget), + &attributes, attributes_mask); + gdk_window_set_user_data (proxy->priv->offscreen_window, widget); + + if (proxy->priv->child) + gtk_widget_set_parent_window (proxy->priv->child, proxy->priv->offscreen_window); + + gdk_offscreen_window_set_embedder (proxy->priv->offscreen_window, + window); + + g_signal_connect(proxy->priv->offscreen_window, "create-surface", + G_CALLBACK (ido_offscreen_proxy_create_alpha_image_surface), + proxy); + g_signal_connect (proxy->priv->offscreen_window, "to-embedder", + G_CALLBACK (offscreen_to_parent), NULL); + g_signal_connect (proxy->priv->offscreen_window, "from-embedder", + G_CALLBACK (offscreen_from_parent), NULL); + + context = gtk_widget_get_style_context (widget); + gtk_style_context_add_class (context, GTK_STYLE_CLASS_MENUITEM); + gtk_style_context_set_background (context, window); + gtk_style_context_set_background (context, proxy->priv->offscreen_window); + + gdk_window_show (proxy->priv->offscreen_window); +} + +static void +ido_offscreen_proxy_unrealize (GtkWidget *widget) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget); + + gdk_window_set_user_data (proxy->priv->offscreen_window, NULL); + gdk_window_destroy (proxy->priv->offscreen_window); + proxy->priv->offscreen_window = NULL; + + GTK_WIDGET_CLASS (ido_offscreen_proxy_parent_class)->unrealize (widget); +} + +static GType +ido_offscreen_proxy_child_type (GtkContainer *container) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container); + + if (proxy->priv->child) + return G_TYPE_NONE; + + return GTK_TYPE_WIDGET; +} + +static void +ido_offscreen_proxy_add (GtkContainer *container, + GtkWidget *child) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container); + + if (!proxy->priv->child) + { + gtk_widget_set_parent_window (child, proxy->priv->offscreen_window); + gtk_widget_set_parent (child, GTK_WIDGET (proxy)); + proxy->priv->child = child; + } + else + { + g_warning ("IdoOffscreenProxy can only have a single child\n"); + } +} + +static void +ido_offscreen_proxy_remove (GtkContainer *container, + GtkWidget *widget) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container); + gboolean was_visible; + + was_visible = gtk_widget_get_visible (widget); + + if (proxy->priv->child == widget) + { + gtk_widget_unparent (widget); + proxy->priv->child = NULL; + + if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container))) + gtk_widget_queue_resize (GTK_WIDGET (container)); + } +} + +static void +ido_offscreen_proxy_forall (GtkContainer *container, + gboolean include_internals, + GtkCallback callback, + gpointer callback_data) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (container); + + g_return_if_fail (callback != NULL); + + if (proxy->priv->child) + (*callback) (proxy->priv->child, callback_data); +} + +static void +ido_offscreen_proxy_size_request (GtkWidget *widget, + GtkRequisition *requisition) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget); + int w, h; + + w = 0; + h = 0; + + if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child)) + { + GtkRequisition child_requisition; + + gtk_widget_get_preferred_size (proxy->priv->child, + &child_requisition, NULL); + w = child_requisition.width; + h = child_requisition.height; + } + + requisition->width = w; + requisition->height = h; + +} + +static void +ido_offscreen_proxy_get_preferred_width (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkRequisition requisition; + + ido_offscreen_proxy_size_request (widget, &requisition); + + *minimum = *natural = requisition.width; +} + +static void +ido_offscreen_proxy_get_preferred_height (GtkWidget *widget, + gint *minimum, + gint *natural) +{ + GtkRequisition requisition; + + ido_offscreen_proxy_size_request (widget, &requisition); + + *minimum = *natural = requisition.height; + +} + +static void +ido_offscreen_proxy_size_allocate (GtkWidget *widget, + GtkAllocation *allocation) +{ + IdoOffscreenProxy *proxy; + + proxy = IDO_OFFSCREEN_PROXY (widget); + + gtk_widget_set_allocation (widget, allocation); + + if (gtk_widget_get_realized (widget)) + { + gdk_window_move_resize (gtk_widget_get_window (widget), + allocation->x, + allocation->y, + allocation->width, + allocation->height); + } + + if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child)) + { + GtkRequisition child_requisition; + GtkAllocation child_allocation; + + gtk_widget_get_preferred_size (proxy->priv->child, + &child_requisition, NULL); + + child_allocation.x = child_requisition.width; + child_allocation.y = child_requisition.height; + child_allocation.width = allocation->width; + child_allocation.height = allocation->height; + + if (gtk_widget_get_realized (widget)) + gdk_window_move_resize (proxy->priv->offscreen_window, + child_allocation.x, + child_allocation.y, + child_allocation.width+4, + child_allocation.height); + + child_allocation.x = child_allocation.y = 0; + gtk_widget_size_allocate (proxy->priv->child, &child_allocation); + } +} + + +static gboolean +ido_offscreen_proxy_damage (GtkWidget *widget, + GdkEventExpose *event) +{ + gdk_window_invalidate_rect (gtk_widget_get_window (widget), + NULL, FALSE); + return TRUE; +} + +static void +get_background_color (GdkRGBA *color) +{ + GtkStyleContext *sc; + GtkWidgetPath *path; + + path = gtk_widget_path_new (); + gtk_widget_path_append_type (path, GTK_TYPE_MENU); + + sc = gtk_style_context_new(); + + gtk_style_context_set_path (sc, path); + gtk_style_context_add_class (sc, "menu"); + gtk_style_context_get_background_color (sc, GTK_STATE_FLAG_ACTIVE, color); + + + gtk_widget_path_free (path); + g_object_unref (sc); +} + +static gboolean +ido_offscreen_proxy_draw (GtkWidget *widget, + cairo_t *cr) +{ + IdoOffscreenProxy *proxy = IDO_OFFSCREEN_PROXY (widget); + GdkWindow *window; + GdkRGBA bg_color; + + window = gtk_widget_get_window (widget); + + get_background_color (&bg_color); + + if (gtk_cairo_should_draw_window (cr, window)) + { + cairo_surface_t *surface; + + if (proxy->priv->child && gtk_widget_get_visible (proxy->priv->child)) + { + surface = gdk_offscreen_window_get_surface (proxy->priv->offscreen_window); + + cairo_set_source_rgba(cr, bg_color.red, bg_color.green, + bg_color.blue, 1); + cairo_paint(cr); + + cairo_set_source_surface (cr, surface, 0, 0); + cairo_paint (cr); + } + } + else if (gtk_cairo_should_draw_window (cr, proxy->priv->offscreen_window)) + { + cairo_save (cr); + cairo_set_operator(cr, CAIRO_OPERATOR_CLEAR); + cairo_paint(cr); + cairo_restore (cr); + + // gtk_render_background (gtk_widget_get_style_context (widget), cr, + // 0, 0, + // gdk_window_get_width (proxy->priv->offscreen_window), + // gdk_window_get_height (proxy->priv->offscreen_window)); + + + + + if (proxy->priv->child) + gtk_container_propagate_draw (GTK_CONTAINER (widget), + proxy->priv->child, + cr); + } + + return TRUE; +} diff --git a/src/idooffscreenproxy.h b/src/idooffscreenproxy.h new file mode 100644 index 0000000..b41bf10 --- /dev/null +++ b/src/idooffscreenproxy.h @@ -0,0 +1,37 @@ +#ifndef __IDO_OFFSCREEN_PROXY_H__ +#define __IDO_OFFSCREEN_PROXy_H__ + +#include +#include + +G_BEGIN_DECLS + +#define IDO_TYPE_OFFSCREEN_PROXY (ido_offscreen_proxy_get_type ()) +#define IDO_OFFSCREEN_PROXY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDO_TYPE_OFFSCREEN_PROXY, IdoOffscreenProxy)) +#define IDO_OFFSCREEN_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDO_TYPE_OFFSCREEN_PROXY, IdoOffscreenProxyClass)) +#define IDO_IS_OFFSCREEN_PROXY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDO_TYPE_OFFSCREEN_PROXY)) +#define IDO_IS_OFFSCREEN_PROXY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDO_TYPE_OFFSCREEN_PROXY)) +#define IDO_OFFSCREEN_PROXY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDO_TYPE_OFFSCREEN_PROXY, IdoOffscreenProxyClass)) + +typedef struct _IdoOffscreenProxy IdoOffscreenProxy; +typedef struct _IdoOffscreenProxyClass IdoOffscreenProxyClass; +typedef struct _IdoOffscreenProxyPrivate IdoOffscreenProxyPrivate; + +struct _IdoOffscreenProxyClass +{ + GtkBinClass parent_class; +}; + +struct _IdoOffscreenProxy +{ + GtkContainer container; + + IdoOffscreenProxyPrivate *priv; +}; + +GType ido_offscreen_proxy_get_type (void) G_GNUC_CONST; +GtkWidget *ido_offscreen_proxy_new (void); + +G_END_DECLS + +#endif diff --git a/src/idoscalemenuitem.c b/src/idoscalemenuitem.c index 8b9e1ac..1f0881a 100644 --- a/src/idoscalemenuitem.c +++ b/src/idoscalemenuitem.c @@ -26,6 +26,7 @@ #include #include "idorange.h" #include "idoscalemenuitem.h" +#include "idooffscreenproxy.h" #include "idotypebuiltins.h" static void ido_scale_menu_item_set_property (GObject *object, @@ -57,6 +58,7 @@ static void update_packing (IdoScaleMenuItem struct _IdoScaleMenuItemPrivate { GtkWidget *scale; + GtkWidget *proxy; GtkAdjustment *adjustment; GtkWidget *primary_image; GtkWidget *secondary_image; @@ -226,6 +228,12 @@ ido_scale_menu_item_constructed (GObject *object) priv->scale = ido_range_new (adj, range_style); g_object_ref (priv->scale); gtk_scale_set_draw_value (GTK_SCALE (priv->scale), FALSE); + + gtk_widget_set_can_focus (priv->scale, FALSE); + + priv->proxy = ido_offscreen_proxy_new (); + g_object_ref (priv->proxy); + gtk_container_add (GTK_CONTAINER (priv->proxy), priv->scale); hbox = gtk_hbox_new (FALSE, 0); @@ -338,23 +346,23 @@ update_packing (IdoScaleMenuItem *self, IdoScaleMenuItemStyle style, IdoScaleMen switch (old_style) { case IDO_SCALE_MENU_ITEM_STYLE_NONE: - gtk_container_remove (container, priv->scale); + gtk_container_remove (container, priv->proxy); break; case IDO_SCALE_MENU_ITEM_STYLE_IMAGE: gtk_container_remove (container, priv->primary_image); gtk_container_remove (container, priv->secondary_image); - gtk_container_remove (container, priv->scale); + gtk_container_remove (container, priv->proxy); break; case IDO_SCALE_MENU_ITEM_STYLE_LABEL: gtk_container_remove (container, priv->primary_label); gtk_container_remove (container, priv->secondary_label); - gtk_container_remove (container, priv->scale); + gtk_container_remove (container, priv->proxy); break; default: - gtk_container_remove (container, priv->scale); + gtk_container_remove (container, priv->proxy); break; } } @@ -362,23 +370,23 @@ update_packing (IdoScaleMenuItem *self, IdoScaleMenuItemStyle style, IdoScaleMen switch (style) { case IDO_SCALE_MENU_ITEM_STYLE_NONE: - gtk_box_pack_start (GTK_BOX (priv->hbox), priv->scale, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->proxy, FALSE, FALSE, 0); break; case IDO_SCALE_MENU_ITEM_STYLE_IMAGE: gtk_box_pack_start (GTK_BOX (priv->hbox), priv->primary_image, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (priv->hbox), priv->scale, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->proxy, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (priv->hbox), priv->secondary_image, FALSE, FALSE, 0); break; case IDO_SCALE_MENU_ITEM_STYLE_LABEL: gtk_box_pack_start (GTK_BOX (priv->hbox), priv->primary_label, FALSE, FALSE, 0); - gtk_box_pack_start (GTK_BOX (priv->hbox), priv->scale, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->proxy, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (priv->hbox), priv->secondary_label, FALSE, FALSE, 0); break; default: - gtk_box_pack_start (GTK_BOX (priv->hbox), priv->scale, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (priv->hbox), priv->proxy, FALSE, FALSE, 0); break; } @@ -480,12 +488,12 @@ ido_scale_menu_item_button_press_event (GtkWidget *menuitem, translate_event_coordinates (menuitem, event->x_root, &x); event->x_root = x; - ubuntu_gtk_widget_set_has_grab (scale, TRUE); + // ubuntu_gtk_widget_set_has_grab (scale, TRUE); - gtk_widget_event (scale, + gtk_widget_event (priv->scale, ((GdkEvent *)(void*)(event))); - ubuntu_gtk_widget_set_has_grab (scale, FALSE); + // ubuntu_gtk_widget_set_has_grab (scale, FALSE); if (!priv->grabbed) { @@ -493,7 +501,7 @@ ido_scale_menu_item_button_press_event (GtkWidget *menuitem, g_signal_emit (menuitem, signals[SLIDER_GRABBED], 0); } - return TRUE; + return FALSE; } static gboolean -- cgit v1.2.3