Chinaunix首页 | 论坛 | 博客
  • 博客访问: 43333
  • 博文数量: 3
  • 博客积分: 321
  • 博客等级: 一等列兵
  • 技术积分: 40
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-25 14:20
文章分类
文章存档

2011年(2)

2010年(1)

我的朋友

分类: Delphi

2011-03-20 21:52:45

Glade是Gtk+的界面设计器,详见:.基本上是一个所见即所得的Gtk+界面设计工具,本文讨论Glade“所见”的实现方法,即下图中正在设计中的界面是怎样画出来的。

就是上上图中被红色矩形框包围的部分是怎样画出来的。

在Gtk+的体系结构中是不允许一个toplevel类型的Widget(即:GtkWindow)中放置一个toplevel类型的Widget的。像下面的代码这样:

  1. GtkWidget *window_embedded = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  2. GtkWidget *window_embedder = gtk_window_new (GTK_WINDOW_TOPLEVEL);

  3. ..........
  4. ..........
  5. 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
  1. #include <gtk/gtk.h>

  2. #include "suppercontainer.h"

  3. struct _SupperContainerPriv
  4. {
  5.   GdkWindow *window;
  6.   GdkWindow *offscreen_window;

  7.   gint inner_border;
  8.   gint child_offset;
  9. };

  10. G_DEFINE_TYPE (SupperContainer, supper_container, GTK_TYPE_BIN)

  11. static void
  12. supper_container_get_preferred_height (GtkWidget *widget,
  13.                                        gint *min, gint *nat)
  14. {
  15.   SupperContainerPriv *priv;
  16.   GtkWidget *child;
  17.   gint child_height = 0;
  18.   gint border_width = 0;

  19.   priv = SUPPER_CONTAINER (widget)->priv;

  20.   *min = 0;

  21.   child = gtk_bin_get_child (GTK_BIN (widget));

  22.   if (child && gtk_widget_get_visible (child))
  23.   {
  24.     GtkRequisition req;

  25.     gtk_widget_get_preferred_size (child, &req, NULL);

  26.     *min = req.height;
  27.   }

  28.   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
  29.   *min += border_width * 2;
  30.   *min += priv->inner_border * 2;

  31.   *nat = *min;
  32. }

  33. static void
  34. supper_container_get_preferred_width (GtkWidget *widget,
  35.                                       gint *min, gint *nat)
  36. {
  37.   SupperContainerPriv *priv;
  38.   GtkWidget *child;
  39.   gint child_height = 0;
  40.   gint border_width = 0;

  41.   priv = SUPPER_CONTAINER (widget)->priv;

  42.   *min = 0;

  43.   child = gtk_bin_get_child (GTK_BIN (widget));

  44.   if (child && gtk_widget_get_visible (child))
  45.   {
  46.     GtkRequisition req;

  47.     gtk_widget_get_preferred_size (child, &req, NULL);

  48.     *min = req.width;
  49.   }

  50.   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));
  51.   *min += border_width * 2;
  52.   *min += priv->inner_border * 2;

  53.   *nat = *min;
  54. }

  55. static void
  56. supper_container_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
  57. {
  58.   GtkWidget *child;

  59.   gtk_widget_set_allocation (widget, allocation);

  60.   if (gtk_widget_get_realized (widget))
  61.     gdk_window_move_resize (gtk_widget_get_window (widget),
  62.      allocation->x, allocation->y,
  63.                             allocation->width, allocation->height);

  64.   child = gtk_bin_get_child (GTK_BIN (widget));

  65.   if (child && gtk_widget_get_visible (child))
  66.   {
  67.     SupperContainerPriv *priv = SUPPER_CONTAINER (widget)->priv;
  68.     GtkAllocation alloc;
  69.     gint height, offset;

  70.     offset = gtk_container_get_border_width (GTK_CONTAINER (widget));
  71.     offset += priv->inner_border;
  72.     priv->child_offset = offset;

  73.     alloc.x = alloc.y = 0;
  74.     alloc.width = allocation->width - offset * 2;
  75.     alloc.height = allocation->height - offset * 2;

  76.     if (gtk_widget_get_realized (widget))
  77.      gdk_window_move_resize (priv->offscreen_window,
  78.      0, 0, alloc.width, alloc.height);
  79.     
  80.     gtk_widget_size_allocate (child, &alloc);
  81.   }
  82. }

  83. static void
  84. supper_container_add (GtkContainer *container, GtkWidget *widget)
  85. {
  86.   SupperContainer *supper_container = SUPPER_CONTAINER (container);
  87.   SupperContainerPriv *priv = supper_container->priv;

  88.   gtk_widget_set_parent_window (widget, priv->offscreen_window);

  89.   GTK_CONTAINER_CLASS (supper_container_parent_class)->add (container, widget);

  90.   gtk_widget_queue_draw (GTK_WIDGET (container));
  91. }

  92. static void
  93. supper_container_remove (GtkContainer *container, GtkWidget *widget)
  94. {
  95.   GTK_CONTAINER_CLASS (supper_container_parent_class)->remove (container, widget);

  96.   gtk_widget_queue_draw (GTK_WIDGET (container));
  97. }

  98. static gboolean
  99. supper_container_damage (GtkWidget *widget, GdkEventExpose *event)
  100. {
  101.   gdk_window_invalidate_rect (gtk_widget_get_window (widget), NULL, TRUE);

  102.   return TRUE;
  103. }

  104. static gboolean
  105. supper_container_draw (GtkWidget *widget, cairo_t *cr)
  106. {
  107.   SupperContainerPriv *priv = SUPPER_CONTAINER (widget)->priv;
  108.   GtkStyleContext *context;
  109.   GtkStateFlags state;
  110.   gint width, height;

  111.   context = gtk_widget_get_style_context (widget);
  112.   state = gtk_widget_get_state_flags (widget);

  113.   gtk_style_context_set_state (context, state);


  114.   if (gtk_cairo_should_draw_window (cr, gtk_widget_get_window (widget)))
  115.   {
  116.     GtkWidget *child;
  117.     GdkRGBA color;

  118.     gdk_window_get_geometry (gtk_widget_get_window (widget), NULL, NULL, &width, &height);

  119.     gtk_render_background (context, cr, 0, 0, width, height);

  120.     child = gtk_bin_get_child (GTK_BIN (widget));

  121.     if (child && gtk_widget_get_visible (child))
  122.     {
  123.      gdk_cairo_set_source_window (cr, priv->offscreen_window,
  124.      priv->child_offset, priv->child_offset);
  125.      cairo_rectangle (cr, priv->child_offset, priv->child_offset,
  126.      gdk_window_get_width (priv->offscreen_window),
  127.                      gdk_window_get_height (priv->offscreen_window));
  128.      cairo_fill (cr);

  129.       /* draw frame border */
  130.      gtk_style_context_get_color (context, state, &color);
  131.       gdk_cairo_set_source_rgba (cr, &color);
  132.       
  133.      cairo_set_line_width (cr, priv->inner_border);
  134.      cairo_rectangle (cr, priv->inner_border, priv->inner_border,
  135.      width - priv->inner_border * 2,
  136.                      height - priv->inner_border * 2);
  137.      cairo_stroke (cr);
  138.     }
  139.   }
  140.   else if (gtk_cairo_should_draw_window (cr, priv->offscreen_window))
  141.   {
  142.     GtkWidget *child = gtk_bin_get_child (GTK_BIN (widget));

  143.     gtk_render_background (context, cr, 0, 0,
  144.      gdk_window_get_width (priv->offscreen_window),
  145.                          gdk_window_get_height (priv->offscreen_window));
  146.     
  147.     if (child)
  148.      gtk_container_propagate_draw (GTK_CONTAINER (widget), child, cr);
  149.   }

  150.   return FALSE;
  151. }

  152. static inline void
  153. to_child (SupperContainer *bin,
  154.           double widget_x,
  155.           double widget_y,
  156.           double *x_out,
  157.           double *y_out)
  158. {
  159.   SupperContainerPriv *priv = SUPPER_CONTAINER (bin)->priv;

  160.   *x_out = widget_x - priv->child_offset;
  161.   *y_out = widget_y - priv->child_offset;
  162. }

  163. static inline void
  164. to_parent (SupperContainer *bin,
  165.            double offscreen_x,
  166.            double offscreen_y,
  167.            double *x_out,
  168.            double *y_out)
  169. {
  170.   SupperContainerPriv *priv = SUPPER_CONTAINER (bin)->priv;
  171.  
  172.   *x_out = offscreen_x + priv->child_offset;
  173.   *y_out = offscreen_y + priv->child_offset;
  174. }

  175. static GdkWindow*
  176. pick_offscreen_child (GdkWindow *window, double widget_x, double widget_y,
  177.                       SupperContainer *bin)
  178. {
  179.   SupperContainerPriv *priv = SUPPER_CONTAINER (bin)->priv;
  180.   GtkWidget *child = gtk_bin_get_child (GTK_BIN (bin));

  181.   if (child && gtk_widget_get_visible (child))
  182.   {
  183.     GtkAllocation child_area;
  184.     double x, y;

  185.     to_child (bin, widget_x, widget_y, &x, &y);

  186.     gtk_widget_get_allocation (child, &child_area);

  187.     if (x >= 0 && x < child_area.width &&
  188.      y >= 0 && y < child_area.height)
  189.      return priv->offscreen_window;
  190.   }

  191.   return NULL;
  192. }

  193. static void
  194. offscreen_window_to_parent (GdkWindow *offscreen_window,
  195.                             double offscreen_x,
  196.                             double offscreen_y,
  197.                             double *parent_x,
  198.                             double *parent_y,
  199.                             SupperContainer *bin)
  200. {
  201.   to_parent (bin, offscreen_x, offscreen_y, parent_x, parent_y);
  202. }

  203. static void
  204. offscreen_window_from_parent (GdkWindow *window,
  205.                               double parent_x,
  206.                               double parent_y,
  207.                               double *offscreen_x,
  208.                               double *offscreen_y,
  209.                               SupperContainer *bin)
  210. {
  211.   to_child (bin, parent_x, parent_y, offscreen_x, offscreen_y);
  212. }

  213. static void
  214. supper_container_realize (GtkWidget *widget)
  215. {
  216.   SupperContainerPriv *priv;
  217.   GtkStyleContext *context;
  218.   GdkWindowAttr attributes;
  219.   GtkWidget *child;
  220.   gint attributes_mask, border_width;
  221.   GtkAllocation allocation;

  222.   priv = SUPPER_CONTAINER (widget)->priv;

  223.   gtk_widget_set_realized (widget, TRUE);

  224.   gtk_widget_get_allocation (widget, &allocation);
  225.   border_width = gtk_container_get_border_width (GTK_CONTAINER (widget));

  226.   attributes.x = allocation.x + border_width;
  227.   attributes.y = allocation.y + border_width;
  228.   attributes.width = allocation.width - 2 * border_width;
  229.   attributes.height = allocation.height - 2 * border_width;
  230.   attributes.window_type = GDK_WINDOW_CHILD;
  231.   attributes.event_mask = gtk_widget_get_events (widget)
  232.                         | GDK_EXPOSURE_MASK
  233.                         | GDK_POINTER_MOTION_MASK
  234.                         | GDK_BUTTON_PRESS_MASK;
  235.   attributes.visual = gtk_widget_get_visual (widget);
  236.   attributes.wclass = GDK_INPUT_OUTPUT;

  237.   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;

  238.   priv->window = gdk_window_new (gtk_widget_get_parent_window (widget),
  239.                                  &attributes, attributes_mask);
  240.   gtk_widget_set_window (widget, priv->window);
  241.   gdk_window_set_user_data (priv->window, widget);

  242.   g_signal_connect (priv->window, "pick-embedded-child",
  243.                     G_CALLBACK (pick_offscreen_child), widget);

  244.   child = gtk_bin_get_child (GTK_BIN (widget));
  245.   attributes.window_type = GDK_WINDOW_OFFSCREEN;
  246.   attributes.x = attributes.y = 0;

  247.   if (child && gtk_widget_get_visible (child))
  248.   {
  249.     GtkAllocation alloc;

  250.     gtk_widget_get_allocation (child, &alloc);
  251.     attributes.width = allocation.width;
  252.     attributes.height = allocation.height;
  253.   }
  254.   else
  255.     attributes.width = attributes.height = 0;

  256.   priv->offscreen_window = gdk_window_new (gtk_widget_get_root_window (widget),
  257.                                            &attributes, attributes_mask);
  258.   gdk_window_set_user_data (priv->offscreen_window, widget);

  259.   if (child)
  260.     gtk_widget_set_parent_window (child, priv->offscreen_window);

  261.   gdk_offscreen_window_set_embedder (priv->offscreen_window, priv->window);

  262.   g_signal_connect (priv->offscreen_window, "to-embedder",
  263.                     G_CALLBACK (offscreen_window_to_parent), widget);
  264.   g_signal_connect (priv->offscreen_window, "from-embedder",
  265.                     G_CALLBACK (offscreen_window_from_parent), widget);

  266.   context = gtk_widget_get_style_context (widget);
  267.   gtk_style_context_set_background (context, priv->window);
  268.   gtk_style_context_set_background (context, priv->offscreen_window);
  269.   gdk_window_show (priv->offscreen_window);
  270. }

  271. static void
  272. supper_container_unrealize (GtkWidget *widget)
  273. {
  274.   SupperContainerPriv *priv = SUPPER_CONTAINER (widget)->priv;

  275.   if (priv->offscreen_window)
  276.   {
  277.     gdk_window_set_user_data (priv->offscreen_window, NULL);
  278.     gdk_window_destroy (priv->offscreen_window);
  279.     priv->offscreen_window = NULL;
  280.   }

  281.   GTK_WIDGET_CLASS (supper_container_parent_class)->unrealize (widget);
  282. }

  283. static void
  284. supper_container_init (SupperContainer *supper)
  285. {
  286.   SupperContainerPriv *priv;

  287.   supper->priv = priv = G_TYPE_INSTANCE_GET_PRIVATE (supper, SUPPER_CONTAINER_TYPE, SupperContainerPriv);

  288.   priv->inner_border = 2;

  289.   gtk_widget_set_has_window (GTK_WIDGET (supper), TRUE);
  290. }

  291. static void
  292. supper_container_class_init (SupperContainerClass *klass)
  293. {
  294.   GObjectClass *object_class;
  295.   GtkWidgetClass *widget_class;
  296.   GtkContainerClass *container_class;

  297.   object_class = G_OBJECT_CLASS (klass);
  298.   widget_class = GTK_WIDGET_CLASS (klass);
  299.   container_class = GTK_CONTAINER_CLASS (klass);

  300.   container_class->add = supper_container_add;
  301.   container_class->remove = supper_container_remove;

  302.   widget_class->realize = supper_container_realize;
  303.   widget_class->unrealize = supper_container_unrealize;
  304.   widget_class->draw = supper_container_draw;
  305.   widget_class->get_preferred_width = supper_container_get_preferred_width;
  306.   widget_class->get_preferred_height = supper_container_get_preferred_height;
  307.   widget_class->size_allocate = supper_container_size_allocate;

  308.   g_signal_override_class_closure (g_signal_lookup ("damage-event", GTK_TYPE_WIDGET),
  309.                                    SUPPER_CONTAINER_TYPE,
  310.                                  g_cclosure_new (G_CALLBACK (supper_container_damage),
  311.                                  NULL, NULL));

  312.   g_type_class_add_private (object_class, sizeof (SupperContainerPriv));
  313. }

  314. GtkWidget*
  315. supper_container_new (void)
  316. {
  317.   return g_object_new (SUPPER_CONTAINER_TYPE, NULL);
  318. }
suppercontainer.h
  1. #ifndef __SUPPER_CONTAINER_H__
  2. #define __SUPPER_CONTAINER_H__

  3. #include <gtk/gtk.h>

  4. G_BEGIN_DECLS

  5. #define SUPPER_CONTAINER_TYPE (supper_container_get_type ())
  6. #define SUPPER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SUPPER_CONTAINER_TYPE, SupperContainer))
  7. #define SUPPER_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), SUPPER_CONTAINER_TYPE, SupperContainerClass))
  8. #define IS_SUPPER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), SUPPER_CONTAINER_TYPE))
  9. #define IS_SUPPER_CONTAINER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), SUPPER_CONTAINER_TYPE))
  10. #define SUPPER_CONTAINER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), SUPPER_CONTAINER_TYPE, SupperContainerClass))

  11. typedef struct _SupperContainer SupperContainer;
  12. typedef struct _SupperContainerPriv SupperContainerPriv;
  13. typedef struct _SupperContainerClass SupperContainerClass;

  14. struct _SupperContainer
  15. {
  16.   GtkBin parent_instance;

  17.   SupperContainerPriv *priv;
  18. };

  19. struct _SupperContainerClass
  20. {
  21.   GtkBinClass parent_class;
  22. };

  23. GType supper_container_get_type (void) G_GNUC_CONST;

  24. GtkWidget* supper_container_new (void);

  25. G_END_DECLS

  26. #endif
测试程序(test.c):
  1. #include <gtk/gtk.h>
  2. #include "suppercontainer.h"

  3. int main (int argc, char *argv[])
  4. {
  5.   GtkWidget *window, *embedded_window;
  6.   GtkWidget *supper;
  7.   GtkWidget *label_embedded;
  8.   GtkWidget *label_embedder;
  9.   GtkWidget *vbox;

  10.   gtk_init (&argc, &argv);

  11.   window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  12.   gtk_widget_set_size_request (window, 400, 300);
  13.   
  14.   vbox = gtk_vbox_new (FALSE, 2);
  15.   gtk_widget_show (vbox);

  16.   label_embedder = gtk_label_new ("I'am a label in embedder.");
  17.   gtk_widget_show (label_embedder);

  18.   gtk_box_pack_start (GTK_BOX(vbox), label_embedder, FALSE, FALSE, 5);

  19.   supper = supper_container_new ();
  20.   gtk_widget_show (supper);

  21.   gtk_box_pack_start (GTK_BOX(vbox), supper, TRUE, TRUE, 5);

  22.   gtk_container_add (GTK_CONTAINER (window), vbox);
  23.   
  24.   gtk_widget_show (window);

  25.   embedded_window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
  26.   label_embedded = gtk_label_new ("I'am a label in embedded.");
  27.   gtk_widget_show (label_embedded);
  28.   gtk_container_add (GTK_CONTAINER (embedded_window), label_embedded);

  29.   gtk_container_add (GTK_CONTAINER (supper), embedded_window);

  30.   gtk_widget_show_all (window);

  31.   gtk_main ();

  32.   return 0;
  33. }
gcc -g -O0 -o test `pkg-config --cflags --libs gtk+-3.0` test.c suppercontainer.c
编译后可测试:

阅读(5669) | 评论(0) | 转发(0) |
0

上一篇:使用xmpp库loudmouth连接google talk服务器

下一篇:没有了

给主人留下些什么吧!~~