static void gtk_offscreen_box_remove (GtkContainer *container, GtkWidget *widget) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container); gboolean was_visible; was_visible = gtk_widget_get_visible (widget); if (offscreen_box->child1 == widget) { gtk_widget_unparent (widget); offscreen_box->child1 = NULL; if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container))) gtk_widget_queue_resize (GTK_WIDGET (container)); } else if (offscreen_box->child2 == widget) { gtk_widget_unparent (widget); offscreen_box->child2 = NULL; if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container))) gtk_widget_queue_resize (GTK_WIDGET (container)); } }
static void gtk_offscreen_box_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); int w, h; w = 0; h = 0; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { GtkRequisition child_requisition; gtk_widget_size_request (offscreen_box->child1, &child_requisition); w = MAX (w, CHILD1_SIZE_SCALE * child_requisition.width); h += CHILD1_SIZE_SCALE * child_requisition.height; } if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { GtkRequisition child_requisition; gtk_widget_size_request (offscreen_box->child2, &child_requisition); w = MAX (w, CHILD2_SIZE_SCALE * child_requisition.width); h += CHILD2_SIZE_SCALE * child_requisition.height; } requisition->width = GTK_CONTAINER (widget)->border_width * 2 + w; requisition->height = GTK_CONTAINER (widget)->border_width * 2 + h; }
static GType gtk_offscreen_box_child_type (GtkContainer *container) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container); if (offscreen_box->child1 && offscreen_box->child2) return G_TYPE_NONE; return GTK_TYPE_WIDGET; }
static void gtk_offscreen_box_add (GtkContainer *container, GtkWidget *widget) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container); if (!offscreen_box->child1) gtk_offscreen_box_add1 (offscreen_box, widget); else if (!offscreen_box->child2) gtk_offscreen_box_add2 (offscreen_box, widget); else g_warning ("GtkOffscreenBox cannot have more than 2 children\n"); }
static void gtk_offscreen_box_forall (GtkContainer *container, gboolean include_internals, GtkCallback callback, gpointer callback_data) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (container); g_return_if_fail (callback != NULL); if (offscreen_box->child1) (*callback) (offscreen_box->child1, callback_data); if (offscreen_box->child2) (*callback) (offscreen_box->child2, callback_data); }
static void gtk_offscreen_box_unrealize (GtkWidget *widget) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); gdk_window_set_user_data (offscreen_box->offscreen_window1, NULL); gdk_window_destroy (offscreen_box->offscreen_window1); offscreen_box->offscreen_window1 = NULL; gdk_window_set_user_data (offscreen_box->offscreen_window2, NULL); gdk_window_destroy (offscreen_box->offscreen_window2); offscreen_box->offscreen_window2 = NULL; GTK_WIDGET_CLASS (gtk_offscreen_box_parent_class)->unrealize (widget); }
static void gtk_offscreen_box_size_request (GtkWidget *widget, GtkRequisition *requisition) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); int w, h; guint border_width; w = 0; h = 0; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { GtkRequisition child_requisition; gtk_widget_get_preferred_size ( (offscreen_box->child1), &child_requisition, NULL); w = MAX (w, CHILD1_SIZE_SCALE * child_requisition.width); h += CHILD1_SIZE_SCALE * child_requisition.height; } if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { GtkRequisition child_requisition; gtk_widget_get_preferred_size ( (offscreen_box->child2), &child_requisition, NULL); w = MAX (w, CHILD2_SIZE_SCALE * child_requisition.width); h += CHILD2_SIZE_SCALE * child_requisition.height; } border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); requisition->width = border_width * 2 + w; requisition->height = border_width * 2 + h; }
static gboolean gtk_offscreen_box_expose (GtkWidget *widget, GdkEventExpose *event) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); if (gtk_widget_is_drawable (widget)) { if (event->window == widget->window) { GdkPixmap *pixmap; GtkAllocation child_area; cairo_t *cr; int start_y = 0; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { pixmap = gdk_offscreen_window_get_pixmap (offscreen_box->offscreen_window1); child_area = offscreen_box->child1->allocation; cr = gdk_cairo_create (widget->window); gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0); cairo_paint (cr); cairo_destroy (cr); start_y += child_area.height; } if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { pixmap = gdk_offscreen_window_get_pixmap (offscreen_box->offscreen_window2); child_area = offscreen_box->child2->allocation; cr = gdk_cairo_create (widget->window); /* transform */ cairo_translate (cr, 0, start_y); cairo_translate (cr, child_area.width / 2, child_area.height / 2); cairo_rotate (cr, offscreen_box->angle); cairo_translate (cr, -child_area.width / 2, -child_area.height / 2); /* paint */ gdk_cairo_set_source_pixmap (cr, pixmap, 0, 0); cairo_paint (cr); cairo_destroy (cr); } } else if (event->window == offscreen_box->offscreen_window1) { gtk_paint_flat_box (widget->style, event->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, &event->area, widget, "blah", 0, 0, -1, -1); if (offscreen_box->child1) gtk_container_propagate_expose (GTK_CONTAINER (widget), offscreen_box->child1, event); } else if (event->window == offscreen_box->offscreen_window2) { gtk_paint_flat_box (widget->style, event->window, GTK_STATE_NORMAL, GTK_SHADOW_NONE, &event->area, widget, "blah", 0, 0, -1, -1); if (offscreen_box->child2) gtk_container_propagate_expose (GTK_CONTAINER (widget), offscreen_box->child2, event); } } return FALSE; }
static void gtk_offscreen_box_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkOffscreenBox *offscreen_box; gint border_width; gint start_y; widget->allocation = *allocation; offscreen_box = GTK_OFFSCREEN_BOX (widget); border_width = GTK_CONTAINER (widget)->border_width; if (gtk_widget_get_realized (widget)) gdk_window_move_resize (widget->window, allocation->x + border_width, allocation->y + border_width, allocation->width - border_width * 2, allocation->height - border_width * 2); start_y = 0; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { GtkRequisition child_requisition; GtkAllocation child_allocation; gtk_widget_get_child_requisition (offscreen_box->child1, &child_requisition); child_allocation.x = child_requisition.width * (CHILD1_SIZE_SCALE - 1.0) / 2; child_allocation.y = start_y + child_requisition.height * (CHILD1_SIZE_SCALE - 1.0) / 2; child_allocation.width = MAX (1, (gint) widget->allocation.width - 2 * border_width); child_allocation.height = child_requisition.height; start_y += CHILD1_SIZE_SCALE * child_requisition.height; if (gtk_widget_get_realized (widget)) gdk_window_move_resize (offscreen_box->offscreen_window1, child_allocation.x, child_allocation.y, child_allocation.width, child_allocation.height); child_allocation.x = child_allocation.y = 0; gtk_widget_size_allocate (offscreen_box->child1, &child_allocation); } if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { GtkRequisition child_requisition; GtkAllocation child_allocation; gtk_widget_get_child_requisition (offscreen_box->child2, &child_requisition); child_allocation.x = child_requisition.width * (CHILD2_SIZE_SCALE - 1.0) / 2; child_allocation.y = start_y + child_requisition.height * (CHILD2_SIZE_SCALE - 1.0) / 2; child_allocation.width = MAX (1, (gint) widget->allocation.width - 2 * border_width); child_allocation.height = child_requisition.height; start_y += CHILD2_SIZE_SCALE * child_requisition.height; if (gtk_widget_get_realized (widget)) gdk_window_move_resize (offscreen_box->offscreen_window2, child_allocation.x, child_allocation.y, child_allocation.width, child_allocation.height); child_allocation.x = child_allocation.y = 0; gtk_widget_size_allocate (offscreen_box->child2, &child_allocation); } }
static void gtk_offscreen_box_realize (GtkWidget *widget) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); GdkWindowAttr attributes; gint attributes_mask; gint border_width; GtkRequisition child_requisition; int start_y = 0; gtk_widget_set_realized (widget, TRUE); border_width = GTK_CONTAINER (widget)->border_width; attributes.x = widget->allocation.x + border_width; attributes.y = widget->allocation.y + border_width; attributes.width = widget->allocation.width - 2 * border_width; attributes.height = widget->allocation.height - 2 * border_width; 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 = gtk_widget_get_visual (widget); attributes.colormap = gtk_widget_get_colormap (widget); attributes.wclass = GDK_INPUT_OUTPUT; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; widget->window = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (widget->window, widget); g_signal_connect (widget->window, "pick-embedded-child", G_CALLBACK (pick_offscreen_child), offscreen_box); attributes.window_type = GDK_WINDOW_OFFSCREEN; /* Child 1 */ attributes.x = attributes.y = 0; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { attributes.width = offscreen_box->child1->allocation.width; attributes.height = offscreen_box->child1->allocation.height; start_y += offscreen_box->child1->allocation.height; } offscreen_box->offscreen_window1 = gdk_window_new (gtk_widget_get_root_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (offscreen_box->offscreen_window1, widget); if (offscreen_box->child1) gtk_widget_set_parent_window (offscreen_box->child1, offscreen_box->offscreen_window1); gdk_offscreen_window_set_embedder (offscreen_box->offscreen_window1, widget->window); g_signal_connect (offscreen_box->offscreen_window1, "to-embedder", G_CALLBACK (offscreen_window_to_parent1), offscreen_box); g_signal_connect (offscreen_box->offscreen_window1, "from-embedder", G_CALLBACK (offscreen_window_from_parent1), offscreen_box); /* Child 2 */ attributes.y = start_y; child_requisition.width = child_requisition.height = 0; if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { attributes.width = offscreen_box->child2->allocation.width; attributes.height = offscreen_box->child2->allocation.height; } offscreen_box->offscreen_window2 = gdk_window_new (gtk_widget_get_root_window (widget), &attributes, attributes_mask); gdk_window_set_user_data (offscreen_box->offscreen_window2, widget); if (offscreen_box->child2) gtk_widget_set_parent_window (offscreen_box->child2, offscreen_box->offscreen_window2); gdk_offscreen_window_set_embedder (offscreen_box->offscreen_window2, widget->window); g_signal_connect (offscreen_box->offscreen_window2, "to-embedder", G_CALLBACK (offscreen_window_to_parent2), offscreen_box); g_signal_connect (offscreen_box->offscreen_window2, "from-embedder", G_CALLBACK (offscreen_window_from_parent2), offscreen_box); widget->style = gtk_style_attach (widget->style, widget->window); gtk_style_set_background (widget->style, widget->window, GTK_STATE_NORMAL); gtk_style_set_background (widget->style, offscreen_box->offscreen_window1, GTK_STATE_NORMAL); gtk_style_set_background (widget->style, offscreen_box->offscreen_window2, GTK_STATE_NORMAL); gdk_window_show (offscreen_box->offscreen_window1); gdk_window_show (offscreen_box->offscreen_window2); }
int main (int argc, char *argv[]) { GtkWidget *window, *widget, *vbox, *button; GtkWidget *offscreen = NULL; gboolean use_offscreen; gtk_init (&argc, &argv); use_offscreen = argc == 1; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); gtk_window_set_default_size (GTK_WINDOW (window), 300,300); g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_container_add (GTK_CONTAINER (window), vbox); scale = gtk_scale_new_with_range (GTK_ORIENTATION_HORIZONTAL, 0, G_PI * 2, 0.01); gtk_box_pack_start (GTK_BOX (vbox), scale, FALSE, FALSE, 0); button = gtk_button_new_with_label ("Remove child 2"); gtk_box_pack_start (GTK_BOX (vbox), button, FALSE, FALSE, 0); if (use_offscreen) { offscreen = gtk_offscreen_box_new (); g_signal_connect (scale, "value_changed", G_CALLBACK (scale_changed), offscreen); } else { offscreen = gtk_paned_new (GTK_ORIENTATION_VERTICAL); } gtk_box_pack_start (GTK_BOX (vbox), offscreen, TRUE, TRUE, 0); widget = create_widgets (); if (use_offscreen) gtk_offscreen_box_add1 (GTK_OFFSCREEN_BOX (offscreen), widget); else gtk_paned_add1 (GTK_PANED (offscreen), widget); widget = create_widgets (); if (1) { GtkWidget *widget2, *box2, *offscreen2; offscreen2 = gtk_offscreen_box_new (); gtk_box_pack_start (GTK_BOX (widget), offscreen2, FALSE, FALSE, 0); g_signal_connect (scale, "value_changed", G_CALLBACK (scale_changed), offscreen2); box2 = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); gtk_offscreen_box_add2 (GTK_OFFSCREEN_BOX (offscreen2), box2); widget2 = gtk_button_new_with_label ("Offscreen in offscreen"); gtk_box_pack_start (GTK_BOX (box2), widget2, FALSE, FALSE, 0); widget2 = gtk_entry_new (); gtk_entry_set_text (GTK_ENTRY (widget2), "Offscreen in offscreen"); gtk_box_pack_start (GTK_BOX (box2), widget2, FALSE, FALSE, 0); } if (use_offscreen) gtk_offscreen_box_add2 (GTK_OFFSCREEN_BOX (offscreen), widget); else gtk_paned_add2 (GTK_PANED (offscreen), widget); gtk_widget_show_all (window); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (remove_clicked), widget); gtk_main (); return 0; }
static gboolean gtk_offscreen_box_draw (GtkWidget *widget, cairo_t *cr) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); GdkWindow *window; window = gtk_widget_get_window (widget); if (gtk_cairo_should_draw_window (cr, window)) { cairo_surface_t *surface; GtkAllocation child_area; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { surface = gdk_offscreen_window_get_surface (offscreen_box->offscreen_window1); cairo_set_source_surface (cr, surface, 0, 0); cairo_paint (cr); gtk_widget_get_allocation (offscreen_box->child1, &child_area); cairo_translate (cr, 0, child_area.height); } if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { surface = gdk_offscreen_window_get_surface (offscreen_box->offscreen_window2); gtk_widget_get_allocation (offscreen_box->child2, &child_area); /* transform */ cairo_translate (cr, child_area.width / 2, child_area.height / 2); cairo_rotate (cr, offscreen_box->angle); cairo_translate (cr, -child_area.width / 2, -child_area.height / 2); /* paint */ cairo_set_source_surface (cr, surface, 0, 0); cairo_paint (cr); } } else if (gtk_cairo_should_draw_window (cr, offscreen_box->offscreen_window1)) { gtk_render_background (gtk_widget_get_style_context (widget), cr, 0, 0, gdk_window_get_width (offscreen_box->offscreen_window1), gdk_window_get_height (offscreen_box->offscreen_window1)); if (offscreen_box->child1) gtk_container_propagate_draw (GTK_CONTAINER (widget), offscreen_box->child1, cr); } else if (gtk_cairo_should_draw_window (cr, offscreen_box->offscreen_window2)) { gtk_render_background (gtk_widget_get_style_context (widget), cr, 0, 0, gdk_window_get_width (offscreen_box->offscreen_window2), gdk_window_get_height (offscreen_box->offscreen_window2)); if (offscreen_box->child2) gtk_container_propagate_draw (GTK_CONTAINER (widget), offscreen_box->child2, cr); } return FALSE; }
static void gtk_offscreen_box_realize (GtkWidget *widget) { GtkOffscreenBox *offscreen_box = GTK_OFFSCREEN_BOX (widget); GtkAllocation allocation, child_area; GdkWindow *window; GdkWindowAttr attributes; gint attributes_mask; guint border_width; GtkRequisition child_requisition; int start_y = 0; gtk_widget_set_realized (widget, TRUE); border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); gtk_widget_get_allocation (widget, &allocation); attributes.x = allocation.x + border_width; attributes.y = allocation.y + border_width; attributes.width = allocation.width - 2 * border_width; attributes.height = allocation.height - 2 * border_width; 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 = 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), offscreen_box); attributes.window_type = GDK_WINDOW_OFFSCREEN; /* Child 1 */ attributes.x = attributes.y = 0; if (offscreen_box->child1 && gtk_widget_get_visible (offscreen_box->child1)) { gtk_widget_get_allocation (offscreen_box->child1, &child_area); attributes.width = child_area.width; attributes.height = child_area.height; start_y += child_area.height; } offscreen_box->offscreen_window1 = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (widget)), &attributes, attributes_mask); gdk_window_set_user_data (offscreen_box->offscreen_window1, widget); if (offscreen_box->child1) gtk_widget_set_parent_window (offscreen_box->child1, offscreen_box->offscreen_window1); gdk_offscreen_window_set_embedder (offscreen_box->offscreen_window1, window); g_signal_connect (offscreen_box->offscreen_window1, "to-embedder", G_CALLBACK (offscreen_window_to_parent1), offscreen_box); g_signal_connect (offscreen_box->offscreen_window1, "from-embedder", G_CALLBACK (offscreen_window_from_parent1), offscreen_box); /* Child 2 */ attributes.y = start_y; child_requisition.width = child_requisition.height = 0; if (offscreen_box->child2 && gtk_widget_get_visible (offscreen_box->child2)) { gtk_widget_get_allocation (offscreen_box->child2, &child_area); attributes.width = child_area.width; attributes.height = child_area.height; } offscreen_box->offscreen_window2 = gdk_window_new (gdk_screen_get_root_window (gtk_widget_get_screen (widget)), &attributes, attributes_mask); gdk_window_set_user_data (offscreen_box->offscreen_window2, widget); if (offscreen_box->child2) gtk_widget_set_parent_window (offscreen_box->child2, offscreen_box->offscreen_window2); gdk_offscreen_window_set_embedder (offscreen_box->offscreen_window2, window); g_signal_connect (offscreen_box->offscreen_window2, "create-surface", G_CALLBACK (gdk_offscreen_box_create_alpha_image_surface), offscreen_box); g_signal_connect (offscreen_box->offscreen_window2, "to-embedder", G_CALLBACK (offscreen_window_to_parent2), offscreen_box); g_signal_connect (offscreen_box->offscreen_window2, "from-embedder", G_CALLBACK (offscreen_window_from_parent2), offscreen_box); gdk_window_show (offscreen_box->offscreen_window1); gdk_window_show (offscreen_box->offscreen_window2); }