Glade是Gtk+的界面设计器,详见:.基本上是一个所见即所得的Gtk+界面设计工具,本文讨论Glade“所见”的实现方法,即下图中正在设计中的界面是怎样画出来的。就是上上图中被红色矩形框包围的部分是怎样画出来的。
在Gtk+的体系结构中是不允许一个toplevel类型的Widget(即:GtkWindow)中放置一个toplevel类型的Widget的。像下面的代码这样:- GtkWidget *window_embedded = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
GtkWidget *window_embedder = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
-
..........
-
..........
-
gtk_container_add (GTK_CONTAINER (window_embedder), window_embedded);
是不能工作的。那么,glade是使用什么神秘的技术实现设计中的界面的绘制的呢?
查看glade的代码,完成这部分工作的是其中的GladeDesignLayout这个类,代码就在 gladeui/glade-design-layout.c 中。其工作原理是:
1)GladeDesignLayout继承自GtkBin作为一个容器,用来容纳toplevel类型的Widget(即:GtkWindow)
2)GladeDesignLayout内部使用一个GDK_WINDOW_OFFSCREEN类型的GdkWindow作为它要容纳的Widget的parent window。这样被嵌入的Widget都绘制在这个offscreen类型的GdkWindow上。
3)GladeDesignLayout再把offscreen gdk window中的内容绘制出来
具体细节:
1)gtk_widget_set_parent_window (window_embedded, offscreen_window)
window_embedded是要嵌入显示的GtkWindow,offscreen_window是GladeDesignLayout内部的那个offscreen gdk window。当上面那句话执行的时候gtk实际上会把window_embedded的toplevel标记去掉,这样window_embedded时间上就和一个GtkContainer(vbox,table,grid之类的)一样了。
2)gtk_container_propagate_draw (window_embedded)
这个函数调用配合上面的gtk_widget_set_parent_window就实现了把window_embedded的内容绘制到了offscreen gdk window中。
最后GladeDesignLayout在上面的基础上再绘制一些辅助元素(第一幅图中的红色矩形框,表示选择的半透明矩形,对于尚未填充内容的容器绘制为placeholder等)
下面是根据GladeDesignLayout缩写的一个Gtk+控件:SupperContainer,之所以叫这个名字,是因为它像GladeDesignLayout一样可以容纳toplevel类型的Widget。suppercontainer.c- #include <gtk/gtk.h>
-
-
#include "suppercontainer.h"
-
-
struct _SupperContainerPriv
-
{
-
GdkWindow *window;
-
GdkWindow *offscreen_window;
-
-
gint inner_border;
-
gint child_offset;
-
};
-
-
G_DEFINE_TYPE (SupperContainer, supper_container, GTK_TYPE_BIN)
-
-
static void
-
supper_container_get_preferred_height (GtkWidget *widget,
-
gint *min, gint *nat)
-
{
-
SupperContainerPriv *priv;
-
GtkWidget *child;
-
gint child_height = 0;
-
gint border_width = 0;
-
-
priv = SUPPER_CONTAINER (widget)->priv;
-
-
*min = 0;
-
-
child = gtk_bin_get_child (GTK_BIN (widget));
-
-
if (child && gtk_widget_get_visible (child))
-
{
-
GtkRequisition req;
-
-
gtk_widget_get_preferred_size (child, &req, NULL);
-
-
*min = req.height;
-
}
-
-
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
-
*min += border_width * 2;
-
*min += priv->inner_border * 2;
-
-
*nat = *min;
-
}
-
-
static void
-
supper_container_get_preferred_width (GtkWidget *widget,
-
gint *min, gint *nat)
-
{
-
SupperContainerPriv *priv;
-
GtkWidget *child;
-
gint child_height = 0;
-
gint border_width = 0;
-
-
priv = SUPPER_CONTAINER (widget)->priv;
-
-
*min = 0;
-
-
child = gtk_bin_get_child (GTK_BIN (widget));
-
-
if (child && gtk_widget_get_visible (child))
-
{
-
GtkRequisition req;
-
-
gtk_widget_get_preferred_size (child, &req, NULL);
-
-
*min = req.width;
-
}
-
-
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
-
*min += border_width * 2;
-
*min += priv->inner_border * 2;
-
-
*nat = *min;
-
}
-
-
static void
-
supper_container_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
-
{
-
GtkWidget *child;
-
-
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);
-
-
child = gtk_bin_get_child (GTK_BIN (widget));
-
-
if (child && gtk_widget_get_visible (child))
-
{
-
SupperContainerPriv *priv = SUPPER_CONTAINER (widget)->priv;
-
GtkAllocation alloc;
-
gint height, offset;
-
-
offset = gtk_container_get_border_width (GTK_CONTAINER (widget));
-
offset += priv->inner_border;
-
priv->child_offset = offset;
-
-
alloc.x = alloc.y = 0;
-
alloc.width = allocation->width - offset * 2;
-
alloc.height = allocation->height - offset * 2;
-
-
if (gtk_widget_get_realized (widget))
-
gdk_window_move_resize (priv->offscreen_window,
-
0, 0, alloc.width, alloc.height);
-
-
gtk_widget_size_allocate (child, &alloc);
-
}
-
}
-
-
static void
-
supper_container_add (GtkContainer *container, GtkWidget *widget)
-
{
-
SupperContainer *supper_container = SUPPER_CONTAINER (container);
-
SupperContainerPriv *priv = supper_container->priv;
-
-
gtk_widget_set_parent_window (widget, priv->offscreen_window);
-
-
GTK_CONTAINER_CLASS (supper_container_parent_class)->add (container, widget);
-
-
gtk_widget_queue_draw (GTK_WIDGET (container));
-
}
-
-
static void
-
supper_container_remove (GtkContainer *container, GtkWidget *widget)
-
{
-
GTK_CONTAINER_CLASS (supper_container_parent_class)->remove (container, widget);
-
-
gtk_widget_queue_draw (GTK_WIDGET (container));
-
}
-
-
static gboolean
-
supper_container_damage (GtkWidget *widget, GdkEventExpose *event)
-
{
-
gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, TRUE);
-
-
return TRUE;
-
}
-
-
static gboolean
-
supper_container_draw (GtkWidget *widget, cairo_t *cr)
-
{
-
SupperContainerPriv *priv = SUPPER_CONTAINER (widget)->priv;
-
GtkStyleContext *context;
-
GtkStateFlags state;
-
gint width, height;
-
-
context = gtk_widget_get_style_context (widget);
-
state = gtk_widget_get_state_flags (widget);
-
-
gtk_style_context_set_state (context, state);
-
-
-
if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
-
{
-
GtkWidget *child;
-
GdkRGBA color;
-
-
gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL, &width, &height);
-
-
gtk_render_background (context, cr, 0, 0, width, height);
-
-
child = gtk_bin_get_child (GTK_BIN (widget));
-
-
if (child && gtk_widget_get_visible (child))
-
{
-
gdk_cairo_set_source_window (cr, priv->offscreen_window,
-
priv->child_offset, priv->child_offset);
-
cairo_rectangle (cr, priv->child_offset, priv->child_offset,
-
gdk_window_get_width (priv->offscreen_window),
-
gdk_window_get_height (priv->offscreen_window));
-
cairo_fill (cr);
-
-
/* draw frame border */
-
gtk_style_context_get_color (context, state, &color);
-
gdk_cairo_set_source_rgba (cr, &color);
-
-
cairo_set_line_width (cr, priv->inner_border);
-
cairo_rectangle (cr, priv->inner_border, priv->inner_border,
-
width - priv->inner_border * 2,
-
height - priv->inner_border * 2);
-
cairo_stroke (cr);
-
}
-
}
-
else if (gtk_cairo_should_draw_window (cr, priv->offscreen_window))
-
{
-
GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));
-
-
gtk_render_background (context, cr, 0, 0,
-
gdk_window_get_width (priv->offscreen_window),
-
gdk_window_get_height (priv->offscreen_window));
-
-
if (child)
-
gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
-
}
-
-
return FALSE;
-
}
-
-
static inline void
-
to_child (SupperContainer *bin,
-
double widget_x,
-
double widget_y,
-
double *x_out,
-
double *y_out)
-
{
-
SupperContainerPriv *priv = SUPPER_CONTAINER (bin)->priv;
-
-
*x_out = widget_x - priv->child_offset;
-
*y_out = widget_y - priv->child_offset;
-
}
-
-
static inline void
-
to_parent (SupperContainer *bin,
-
double offscreen_x,
-
double offscreen_y,
-
double *x_out,
-
double *y_out)
-
{
-
SupperContainerPriv *priv = SUPPER_CONTAINER (bin)->priv;
-
-
*x_out = offscreen_x + priv->child_offset;
-
*y_out = offscreen_y + priv->child_offset;
-
}
-
-
static GdkWindow*
-
pick_offscreen_child (GdkWindow *window, double widget_x, double widget_y,
-
SupperContainer *bin)
-
{
-
SupperContainerPriv *priv = SUPPER_CONTAINER (bin)->priv;
-
GtkWidget *child = gtk_bin_get_child (GTK_BIN (bin));
-
-
if (child && gtk_widget_get_visible (child))
-
{
-
GtkAllocation child_area;
-
double x, y;
-
-
to_child (bin, widget_x, widget_y, &x, &y);
-
-
gtk_widget_get_allocation (child, &child_area);
-
-
if (x >= 0 && x < child_area.width &&
-
y >= 0 && y < child_area.height)
-
return priv->offscreen_window;
-
}
-
-
return NULL;
-
}
-
-
static void
-
offscreen_window_to_parent (GdkWindow *offscreen_window,
-
double offscreen_x,
-
double offscreen_y,
-
double *parent_x,
-
double *parent_y,
-
SupperContainer *bin)
-
{
-
to_parent (bin, offscreen_x, offscreen_y, parent_x, parent_y);
-
}
-
-
static void
-
offscreen_window_from_parent (GdkWindow *window,
-
double parent_x,
-
double parent_y,
-
double *offscreen_x,
-
double *offscreen_y,
-
SupperContainer *bin)
-
{
-
to_child (bin, parent_x, parent_y, offscreen_x, offscreen_y);
-
}
-
-
static void
-
supper_container_realize (GtkWidget *widget)
-
{
-
SupperContainerPriv *priv;
-
GtkStyleContext *context;
-
GdkWindowAttr attributes;
-
GtkWidget *child;
-
gint attributes_mask, border_width;
-
GtkAllocation allocation;
-
-
priv = SUPPER_CONTAINER (widget)->priv;
-
-
gtk_widget_set_realized (widget, TRUE);
-
-
gtk_widget_get_allocation (widget, &allocation);
-
border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
-
-
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;
-
attributes.visual = gtk_widget_get_visual (widget);
-
attributes.wclass = GDK_INPUT_OUTPUT;
-
-
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
-
-
priv->window = gdk_window_new (gtk_widget_get_parent_window (widget),
-
&attributes, attributes_mask);
-
gtk_widget_set_window (widget, priv->window);
-
gdk_window_set_user_data (priv->window, widget);
-
-
g_signal_connect (priv->window, "pick-embedded-child",
-
G_CALLBACK (pick_offscreen_child), widget);
-
-
child = gtk_bin_get_child (GTK_BIN (widget));
-
attributes.window_type = GDK_WINDOW_OFFSCREEN;
-
attributes.x = attributes.y = 0;
-
-
if (child && gtk_widget_get_visible (child))
-
{
-
GtkAllocation alloc;
-
-
gtk_widget_get_allocation (child, &alloc);
-
attributes.width = allocation.width;
-
attributes.height = allocation.height;
-
}
-
else
-
attributes.width = attributes.height = 0;
-
-
priv->offscreen_window = gdk_window_new (gtk_widget_get_root_window (widget),
-
&attributes, attributes_mask);
-
gdk_window_set_user_data (priv->offscreen_window, widget);
-
-
if (child)
-
gtk_widget_set_parent_window (child, priv->offscreen_window);
-
-
gdk_offscreen_window_set_embedder (priv->offscreen_window, priv->window);
-
-
g_signal_connect (priv->offscreen_window, "to-embedder",
-
G_CALLBACK (offscreen_window_to_parent), widget);
-
g_signal_connect (priv->offscreen_window, "from-embedder",
-
G_CALLBACK (offscreen_window_from_parent), widget);
-
-
context = gtk_widget_get_style_context (widget);
-
gtk_style_context_set_background (context, priv->window);
-
gtk_style_context_set_background (context, priv->offscreen_window);
-
gdk_window_show (priv->offscreen_window);
-
}
-
-
static void
-
supper_container_unrealize (GtkWidget *widget)
-
{
-
SupperContainerPriv *priv = SUPPER_CONTAINER (widget)->priv;
-
-
if (priv->offscreen_window)
-
{
-
gdk_window_set_user_data (priv->offscreen_window, NULL);
-
gdk_window_destroy (priv->offscreen_window);
-
priv->offscreen_window = NULL;
-
}
-
-
GTK_WIDGET_CLASS (supper_container_parent_class)->unrealize (widget);
-
}
-
-
static void
-
supper_container_init (SupperContainer *supper)
-
{
-
SupperContainerPriv *priv;
-
-
supper->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (supper, SUPPER_CONTAINER_TYPE, SupperContainerPriv);
-
-
priv->inner_border = 2;
-
-
gtk_widget_set_has_window (GTK_WIDGET (supper), TRUE);
-
}
-
-
static void
-
supper_container_class_init (SupperContainerClass *klass)
-
{
-
GObjectClass *object_class;
-
GtkWidgetClass *widget_class;
-
GtkContainerClass *container_class;
-
-
object_class = G_OBJECT_CLASS (klass);
-
widget_class = GTK_WIDGET_CLASS (klass);
-
container_class = GTK_CONTAINER_CLASS (klass);
-
-
container_class->add = supper_container_add;
-
container_class->remove = supper_container_remove;
-
-
widget_class->realize = supper_container_realize;
-
widget_class->unrealize = supper_container_unrealize;
-
widget_class->draw = supper_container_draw;
-
widget_class->get_preferred_width = supper_container_get_preferred_width;
-
widget_class->get_preferred_height = supper_container_get_preferred_height;
-
widget_class->size_allocate = supper_container_size_allocate;
-
-
g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
-
SUPPER_CONTAINER_TYPE,
-
g_cclosure_new (G_CALLBACK (supper_container_damage),
-
NULL, NULL));
-
-
g_type_class_add_private (object_class, sizeof (SupperContainerPriv));
-
}
-
-
GtkWidget*
-
supper_container_new (void)
-
{
-
return g_object_new (SUPPER_CONTAINER_TYPE, NULL);
-
}
suppercontainer.h- #ifndef __SUPPER_CONTAINER_H__
-
#define __SUPPER_CONTAINER_H__
-
-
#include <gtk/gtk.h>
-
-
G_BEGIN_DECLS
-
-
#define SUPPER_CONTAINER_TYPE (supper_container_get_type ())
-
#define SUPPER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SUPPER_CONTAINER_TYPE, SupperContainer))
-
#define SUPPER_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SUPPER_CONTAINER_TYPE, SupperContainerClass))
-
#define IS_SUPPER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SUPPER_CONTAINER_TYPE))
-
#define IS_SUPPER_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUPPER_CONTAINER_TYPE))
-
#define SUPPER_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SUPPER_CONTAINER_TYPE, SupperContainerClass))
-
-
typedef struct _SupperContainer SupperContainer;
-
typedef struct _SupperContainerPriv SupperContainerPriv;
-
typedef struct _SupperContainerClass SupperContainerClass;
-
-
struct _SupperContainer
-
{
-
GtkBin parent_instance;
-
-
SupperContainerPriv *priv;
-
};
-
-
struct _SupperContainerClass
-
{
-
GtkBinClass parent_class;
-
};
-
-
GType supper_container_get_type (void) G_GNUC_CONST;
-
-
GtkWidget* supper_container_new (void);
-
-
G_END_DECLS
-
-
#endif
测试程序(test.c):- #include <gtk/gtk.h>
-
#include "suppercontainer.h"
-
-
int main (int argc, char *argv[])
-
{
-
GtkWidget *window, *embedded_window;
-
GtkWidget *supper;
-
GtkWidget *label_embedded;
-
GtkWidget *label_embedder;
-
GtkWidget *vbox;
-
-
gtk_init (&argc, &argv);
-
-
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
gtk_widget_set_size_request (window, 400, 300);
-
-
vbox = gtk_vbox_new (FALSE, 2);
-
gtk_widget_show (vbox);
-
-
label_embedder = gtk_label_new ("I'am a label in embedder.");
-
gtk_widget_show (label_embedder);
-
-
gtk_box_pack_start (GTK_BOX(vbox), label_embedder, FALSE, FALSE, 5);
-
-
supper = supper_container_new ();
-
gtk_widget_show (supper);
-
-
gtk_box_pack_start (GTK_BOX(vbox), supper, TRUE, TRUE, 5);
-
-
gtk_container_add (GTK_CONTAINER (window), vbox);
-
-
gtk_widget_show (window);
-
-
embedded_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
-
label_embedded = gtk_label_new ("I'am a label in embedded.");
-
gtk_widget_show (label_embedded);
-
gtk_container_add (GTK_CONTAINER (embedded_window), label_embedded);
-
-
gtk_container_add (GTK_CONTAINER (supper), embedded_window);
-
-
gtk_widget_show_all (window);
-
-
gtk_main ();
-
-
return 0;
-
}
gcc -g -O0 -o test `pkg-config --cflags --libs gtk+-3.0` test.c suppercontainer.c
编译后可测试:
阅读(5649) | 评论(0) | 转发(0) |