static void widget_overlay_add (GtkContainer *container, GtkWidget *widget) { WidgetOverlay *ovl = WIDGET_OVERLAY (container); ChildData *cd; cd = g_new0 (ChildData, 1); gtk_widget_set_parent (widget, GTK_WIDGET (ovl)); cd->ovl = ovl; cd->child = widget; cd->halign = WIDGET_OVERLAY_ALIGN_CENTER; cd->valign = WIDGET_OVERLAY_ALIGN_END; cd->alpha = 1.; cd->scale = 1.; cd->ignore_events = FALSE; cd->is_tooltip = FALSE; ovl->priv->children = g_list_append (ovl->priv->children, cd); if (ovl->priv->scale_child) { ChildData *fcd; fcd = get_first_child (ovl); if (cd == fcd) gtk_range_set_value (ovl->priv->scale_range, cd->scale); ovl->priv->children = g_list_remove (ovl->priv->children, ovl->priv->scale_child); ovl->priv->children = g_list_append (ovl->priv->children, ovl->priv->scale_child); } }
static void widget_overlay_dispose (GObject *obj) { WidgetOverlay *ovl = WIDGET_OVERLAY (obj); if (ovl->priv) { if (ovl->priv->idle_timer) { g_source_remove (ovl->priv->idle_timer); ovl->priv->idle_timer = 0; } } if (G_OBJECT_CLASS (parent_class)->dispose) G_OBJECT_CLASS (parent_class)->dispose (obj); }
static void widget_overlay_show (GtkWidget *widget) { WidgetOverlay *ovl = WIDGET_OVERLAY (widget); GList *list; ((GtkWidgetClass *)parent_class)->show (widget); for (list = ovl->priv->children; list; list = list->next) { ChildData *cd; cd = (ChildData*) list->data; if (cd->is_tooltip) gtk_widget_hide (cd->child); } }
static void widget_overlay_unrealize (GtkWidget *widget) { WidgetOverlay *ovl = WIDGET_OVERLAY (widget); GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; gdk_window_set_user_data (cd->offscreen_window, NULL); gdk_window_destroy (cd->offscreen_window); cd->offscreen_window = NULL; } GTK_WIDGET_CLASS (widget_overlay_parent_class)->unrealize (widget); }
static void widget_overlay_size_request (GtkWidget *widget, GtkRequisition *req_min, GtkRequisition *req_nat) { WidgetOverlay *ovl = WIDGET_OVERLAY (widget); gint border_width; border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); req_min->width = 1; req_min->height = 1; req_nat->width = 1; req_nat->height = 1; GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; if (gtk_widget_get_visible (cd->child) && !cd->is_tooltip) { GtkRequisition child_req_min, child_req_nat; gtk_widget_get_preferred_size (cd->child, &child_req_min, &child_req_nat); if (cd->halign == WIDGET_OVERLAY_ALIGN_FILL) { req_min->width = MAX (border_width * 2 + child_req_min.width, req_min->width); req_nat->width = MAX (border_width * 2 + child_req_nat.width, req_nat->width); } else { req_min->width = MAX (border_width * 2 + child_req_min.width * cd->scale, req_min->width); req_nat->width = MAX (border_width * 2 + child_req_nat.width * cd->scale, req_nat->width); } if (cd->valign == WIDGET_OVERLAY_ALIGN_FILL) { req_min->height = MAX (border_width * 2 + child_req_min.height, req_min->height); req_nat->height = MAX (border_width * 2 + child_req_nat.height, req_nat->height); } else { req_min->height = MAX (border_width * 2 + child_req_min.height * cd->scale, req_min->height); req_nat->height = MAX (border_width * 2 + child_req_nat.height * cd->scale, req_nat->height); } } } }
static void widget_overlay_forall (GtkContainer *container, G_GNUC_UNUSED gboolean include_internals, GtkCallback callback, gpointer callback_data) { WidgetOverlay *ovl = WIDGET_OVERLAY (container); g_return_if_fail (callback != NULL); GList *list, *copy; copy = g_list_copy (ovl->priv->children); for (list = copy; list; list = list->next) { ChildData *cd = (ChildData*) list->data; (*callback) (cd->child, callback_data); } g_list_free (copy); }
static void widget_overlay_finalize (GObject *obj) { WidgetOverlay *ovl = WIDGET_OVERLAY (obj); if (ovl->priv->children) { GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; g_free (cd); } g_list_free (ovl->priv->children); } g_free (ovl->priv); if (G_OBJECT_CLASS (parent_class)->finalize) G_OBJECT_CLASS (parent_class)->finalize (obj); }
static void widget_overlay_remove (GtkContainer *container, GtkWidget *widget) { WidgetOverlay *ovl = WIDGET_OVERLAY (container); gboolean was_visible; was_visible = gtk_widget_get_visible (widget); GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; if (cd->child == widget) { gtk_widget_unparent (widget); ovl->priv->children = g_list_remove (ovl->priv->children, cd); g_free (cd); if (was_visible && gtk_widget_get_visible (GTK_WIDGET (container))) gtk_widget_queue_resize (GTK_WIDGET (container)); break; } } }
static void widget_overlay_get_property (GObject *object, guint param_id, GValue *value, GParamSpec *pspec) { WidgetOverlay *ovl = WIDGET_OVERLAY (object); if (ovl->priv) { switch (param_id) { case PROP_ADD_SCALE: { gboolean has_scale = FALSE; if (ovl->priv->scale_child && (ovl->priv->scale_child->alpha > 0.)) has_scale = TRUE; g_value_set_boolean (value, has_scale); break; } } } }
static gboolean widget_overlay_draw (GtkWidget *widget, cairo_t *cr) { WidgetOverlay *ovl = WIDGET_OVERLAY (widget); GdkWindow *window; GtkAllocation area; gtk_widget_get_allocation (widget, &area); window = gtk_widget_get_window (widget); if (gtk_cairo_should_draw_window (cr, window)) { GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; if (gtk_widget_get_visible (cd->child)) { cairo_surface_t *surface; GtkAllocation child_area; double x, y; gtk_widget_get_allocation (cd->child, &child_area); child_area.width *= cd->scale; child_area.height *= cd->scale; switch (cd->halign) { case WIDGET_OVERLAY_ALIGN_FILL: case WIDGET_OVERLAY_ALIGN_START: x = 0; break; case WIDGET_OVERLAY_ALIGN_END: x = area.width - child_area.width; break; case WIDGET_OVERLAY_ALIGN_CENTER: x = (area.width - child_area.width) / 2.; break; } switch (cd->valign) { case WIDGET_OVERLAY_ALIGN_FILL: case WIDGET_OVERLAY_ALIGN_START: y = 0; break; case WIDGET_OVERLAY_ALIGN_END: y = area.height - child_area.height; break; case WIDGET_OVERLAY_ALIGN_CENTER: y = (area.height - child_area.height) / 2.; break; } surface = gdk_offscreen_window_get_surface (cd->offscreen_window); if (cd->scale == 1.) { cairo_set_source_surface (cr, surface, x, y); cairo_paint_with_alpha (cr, cd->alpha); } else { cairo_save (cr); cairo_scale (cr, cd->scale, cd->scale); cairo_set_source_surface (cr, surface, x/cd->scale, y/cd->scale); cairo_paint_with_alpha (cr, cd->alpha); cairo_restore (cr); } cd->x = x; cd->y = y; } if (list->next && ((ChildData*) list->next->data == ovl->priv->scale_child) && (ovl->priv->scale_child->alpha > 0.)) { cairo_set_source_rgba (cr, 0., 0., 0., .3); cairo_rectangle (cr, 0, 0, area.width, area.height); cairo_fill (cr); } } } else { GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; if (gtk_cairo_should_draw_window (cr, cd->offscreen_window)) gtk_container_propagate_draw (GTK_CONTAINER (widget), cd->child, cr); } } return TRUE; }
static void widget_overlay_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { WidgetOverlay *ovl = WIDGET_OVERLAY (widget); gint border_width; gint w, h; gtk_widget_set_allocation (widget, allocation); border_width = gtk_container_get_border_width (GTK_CONTAINER (widget)); w = allocation->width - border_width * 2; h = allocation->height - border_width * 2; if (gtk_widget_get_realized (widget)) { GdkWindow *win; win = gtk_widget_get_window (widget); gdk_window_move_resize (win, allocation->x + border_width, allocation->y + border_width, w, h); } GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; if (gtk_widget_get_visible (cd->child)){ GtkRequisition child_requisition; GtkAllocation child_allocation; gtk_widget_get_preferred_size (cd->child, &child_requisition, NULL); child_allocation.x = 0; child_allocation.y = 0; child_allocation.height = child_requisition.height; child_allocation.width = child_requisition.width; if (cd->halign == WIDGET_OVERLAY_ALIGN_FILL) child_allocation.width = w / cd->scale; if ((cd->valign == WIDGET_OVERLAY_ALIGN_FILL) || (cd == ovl->priv->scale_child)) child_allocation.height = h / cd->scale; if (cd->is_tooltip) { cd->scale = 1.; if ((allocation->width > 0) && (allocation->height > 0)) { if (child_allocation.width > allocation->width) cd->scale = (gdouble) allocation->width / (gdouble) child_allocation.width; if (child_allocation.height > allocation->height) { if (cd->scale > ((gdouble) allocation->height / (gdouble) child_allocation.height)) cd->scale = (gdouble) allocation->height / (gdouble) child_allocation.height; } } } if (gtk_widget_get_realized (widget)) gdk_window_move_resize (cd->offscreen_window, child_allocation.x, child_allocation.y, child_allocation.width, child_allocation.height); child_allocation.x = child_allocation.y = 0; gtk_widget_size_allocate (cd->child, &child_allocation); } } }
static void widget_overlay_realize (GtkWidget *widget) { WidgetOverlay *ovl = WIDGET_OVERLAY (widget); GdkWindowAttr attributes; gint attributes_mask; gint border_width; GtkRequisition child_requisition; GtkAllocation allocation; 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; GdkWindow *win; win = gdk_window_new (gtk_widget_get_parent_window (widget), &attributes, attributes_mask); gtk_widget_set_window (widget, win); gdk_window_set_user_data (win, widget); g_signal_connect (win, "pick-embedded-child", G_CALLBACK (pick_offscreen_child), ovl); GtkStyleContext *style; style = gtk_widget_get_style_context (widget); gtk_style_context_set_background (style, win); /* offscreen windows */ attributes.window_type = GDK_WINDOW_OFFSCREEN; GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; child_requisition.width = child_requisition.height = 0; if (gtk_widget_get_visible (cd->child)) { GtkAllocation allocation; gtk_widget_get_allocation (cd->child, &allocation); attributes.width = allocation.width; attributes.height = allocation.height; } cd->offscreen_window = gdk_window_new (NULL, &attributes, attributes_mask); gdk_window_set_user_data (cd->offscreen_window, widget); gtk_widget_set_parent_window (cd->child, cd->offscreen_window); gdk_offscreen_window_set_embedder (cd->offscreen_window, win); g_signal_connect (cd->offscreen_window, "to-embedder", G_CALLBACK (offscreen_window_to_parent), ovl); g_signal_connect (cd->offscreen_window, "from-embedder", G_CALLBACK (offscreen_window_from_parent), ovl); gtk_style_context_set_background (style, cd->offscreen_window); gdk_window_show (cd->offscreen_window); } }
static gboolean widget_overlay_event (GtkWidget *widget, GdkEvent *event) { GdkEventScroll *ev = (GdkEventScroll *) event; WidgetOverlay *ovl = WIDGET_OVERLAY (widget); #ifdef GDA_DEBUG_NO ChildData *cdevent = NULL; /* find child */ GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd; cd = (ChildData*) list->data; if (cd->offscreen_window == ((GdkEventAny*)event)->window) { cdevent = cd; break; } } g_print (" CH%d/%d", g_list_index (ovl->priv->children, cdevent), event->type); #endif /* tooltip widgets handling */ gboolean tooltip_event = FALSE; if ((event->type == GDK_BUTTON_PRESS) || (event->type == GDK_2BUTTON_PRESS) || (event->type == GDK_3BUTTON_PRESS) || (event->type == GDK_BUTTON_RELEASE) || (event->type == GDK_MOTION_NOTIFY) || (event->type == GDK_KEY_PRESS) || (event->type == GDK_KEY_RELEASE) || (event->type == GDK_ENTER_NOTIFY) || (event->type == GDK_LEAVE_NOTIFY) || (event->type == GDK_SCROLL)) tooltip_event = TRUE; if (tooltip_event) { /*g_print (".");*/ gboolean need_tooltip = FALSE; GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd; cd = (ChildData*) list->data; if (cd->is_tooltip) { need_tooltip = TRUE; break; } } if (ovl->priv->idle_timer) { g_source_remove (ovl->priv->idle_timer); ovl->priv->idle_timer = 0; } if ((event->type != GDK_ENTER_NOTIFY) && (event->type != GDK_LEAVE_NOTIFY)) { gboolean inc_timeout = FALSE; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd; cd = (ChildData*) list->data; if (cd->is_tooltip && gtk_widget_get_visible (cd->child)) { inc_timeout = TRUE; gtk_widget_hide (cd->child); } } if (inc_timeout) { GtkSettings *settings; guint t; settings = gtk_widget_get_settings (widget); g_object_get (settings, "gtk-tooltip-timeout", &t, NULL); if ((ovl->priv->tooltip_ms * 2) < (t * 32)) ovl->priv->tooltip_ms = ovl->priv->tooltip_ms * 2; } } if ((event->type == GDK_LEAVE_NOTIFY) && ((GdkEventAny*)event)->window == gtk_widget_get_window (GTK_WIDGET (ovl))) need_tooltip = FALSE; if (need_tooltip) { if (ovl->priv->tooltip_ms == 0) { GtkSettings *settings; settings = gtk_widget_get_settings (widget); g_object_get (settings, "gtk-tooltip-timeout", &(ovl->priv->tooltip_ms), NULL); } ovl->priv->idle_timer = g_timeout_add (ovl->priv->tooltip_ms, (GSourceFunc) idle_timer_cb, ovl); } } if ((event->type != GDK_SCROLL) || !(ev->state & GDK_SHIFT_MASK)) return FALSE; ChildData *cd = NULL; cd = get_first_child (ovl); if (!cd) return FALSE; gdouble scale; scale = cd->scale; if (ev->direction == GDK_SCROLL_UP) scale += SCALE_STEP; else scale -= SCALE_STEP; change_widget_scale (ovl, cd, scale); return TRUE; }
static void widget_overlay_set_property (GObject *object, guint param_id, const GValue *value, GParamSpec *pspec) { WidgetOverlay *ovl = WIDGET_OVERLAY (object); if (ovl->priv) { switch (param_id) { case PROP_ADD_SCALE: { gboolean need_scale; need_scale = g_value_get_boolean (value); if (!need_scale && !ovl->priv->scale_child) return; if (need_scale && ovl->priv->scale_child) { widget_overlay_set_child_props (ovl, ovl->priv->scale_child->child, WIDGET_OVERLAY_CHILD_ALPHA, .6, -1); } else if (need_scale) { GtkWidget *box, *wid, *button, *image; box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); wid = gtk_scale_new_with_range (GTK_ORIENTATION_VERTICAL, SCALE_MIN, SCALE_MAX, SCALE_STEP); ovl->priv->scale_range = GTK_RANGE (wid); g_object_set (G_OBJECT (wid), "draw-value", FALSE, NULL); gtk_box_pack_start (GTK_BOX (box), wid, TRUE, TRUE, 0); button = gtk_button_new (); image = gtk_image_new_from_icon_name ("window-close-symbolic", GTK_ICON_SIZE_MENU); gtk_container_add (GTK_CONTAINER (button), image); gtk_container_add (GTK_CONTAINER (box), button); gtk_widget_set_name (button, "browser-tab-close-button"); g_signal_connect (button, "clicked", G_CALLBACK (scale_button_clicked_cb), ovl); gtk_container_add (GTK_CONTAINER (ovl), box); gtk_widget_show_all (box); GList *list; for (list = ovl->priv->children; list; list = list->next) { ChildData *cd = (ChildData*) list->data; if (cd->child == box) { ovl->priv->scale_child = cd; break; } } g_assert (ovl->priv->scale_child); ChildData *cd; cd = get_first_child (ovl); if (cd) gtk_range_set_value (ovl->priv->scale_range, cd->scale); gtk_range_set_inverted (ovl->priv->scale_range, TRUE); g_signal_connect (wid, "value-changed", G_CALLBACK (scale_value_changed_cb), ovl); widget_overlay_set_child_props (ovl, box, WIDGET_OVERLAY_CHILD_VALIGN, WIDGET_OVERLAY_ALIGN_FILL, WIDGET_OVERLAY_CHILD_HALIGN, WIDGET_OVERLAY_ALIGN_END, WIDGET_OVERLAY_CHILD_SCALE, 1., WIDGET_OVERLAY_CHILD_ALPHA, .6, -1); } else { widget_overlay_set_child_props (ovl, ovl->priv->scale_child->child, WIDGET_OVERLAY_CHILD_ALPHA, 0., -1); } break; } } } }
static void ui_formgrid_show (GtkWidget *widget) { UiFormGrid *formgrid; GtkWidget *ovl, *packed; formgrid = UI_FORMGRID (widget); if (! formgrid->priv->overlay_grid) { /* finalize packing */ GtkWidget *sw, *vp; sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER (sw), formgrid->priv->raw_grid); gtk_widget_show_all (sw); packed = sw; /* overlay */ ovl = widget_overlay_new (); formgrid->priv->overlay_grid = ovl; g_object_set (G_OBJECT (ovl), "add-scale", TRUE, NULL); g_object_set (G_OBJECT (ovl), "add-scale", FALSE, NULL); gtk_container_add (GTK_CONTAINER (ovl), packed); widget_overlay_set_child_props (WIDGET_OVERLAY (ovl), packed, WIDGET_OVERLAY_CHILD_HALIGN, WIDGET_OVERLAY_ALIGN_FILL, WIDGET_OVERLAY_CHILD_VALIGN, WIDGET_OVERLAY_ALIGN_FILL, WIDGET_OVERLAY_CHILD_SCALE, .9, -1); gtk_widget_show (ovl); gtk_notebook_append_page (GTK_NOTEBOOK (formgrid->priv->nb), ovl, NULL); } if (! formgrid->priv->overlay_form) { /* finalize packing */ if (formgrid->priv->scroll_form) { GtkWidget *sw, *vp; sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_NONE); vp = gtk_viewport_new (NULL, NULL); gtk_widget_set_name (vp, "gdaui-transparent-background"); gtk_container_add (GTK_CONTAINER (sw), vp); gtk_viewport_set_shadow_type (GTK_VIEWPORT (vp), GTK_SHADOW_NONE); gtk_container_add (GTK_CONTAINER (vp), formgrid->priv->raw_form); gtk_widget_show_all (sw); packed = sw; } else { gtk_widget_show (formgrid->priv->raw_form); packed = formgrid->priv->raw_form; } /* overlay */ ovl = widget_overlay_new (); formgrid->priv->overlay_form = ovl; g_object_set (G_OBJECT (ovl), "add-scale", TRUE, NULL); g_object_set (G_OBJECT (ovl), "add-scale", FALSE, NULL); gtk_container_add (GTK_CONTAINER (ovl), packed); widget_overlay_set_child_props (WIDGET_OVERLAY (ovl), packed, WIDGET_OVERLAY_CHILD_HALIGN, WIDGET_OVERLAY_ALIGN_FILL, WIDGET_OVERLAY_CHILD_VALIGN, WIDGET_OVERLAY_ALIGN_FILL, WIDGET_OVERLAY_CHILD_SCALE, 1., -1); gtk_widget_show (ovl); gtk_notebook_append_page (GTK_NOTEBOOK (formgrid->priv->nb), ovl, NULL); gtk_notebook_set_current_page (GTK_NOTEBOOK (formgrid->priv->nb), 0); } ((GtkWidgetClass *)parent_class)->show (widget); if (! formgrid->priv->autoupdate_possible) gtk_widget_hide (formgrid->priv->autoupdate_toggle); }