GTK3自定义单元格渲染器,输出小部件 [英] GTK3 custom cell renderer, that outputs widgets

查看:90
本文介绍了GTK3自定义单元格渲染器,输出小部件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在寻找一种在TreeView单元中输出随机窗口小部件的方法.

I'm looking for a way to output random widgets in the TreeView cells.

这些小部件的生命周期是由我在父级TreeView外部手动控制的100%.到目前为止,您无需担心键盘导航和可访问性.我现在得到的是经过修改的官方示例.

The lifecycle of these widgets is 100% controlled by me manually outside of the parent TreeView. Don't care about keyboard navigation and accessibility (so far). What I got by now is a modified official example.

标题:

#include <gtk/gtk.h>

#define TYPE_RECKLESS_CELL_RENDERER             (reckless_cell_renderer_get_type())
#define RECKLESS_CELL_RENDERER(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj),  TYPE_RECKLESS_CELL_RENDERER, RecklessCellRenderer))
#define RECKLESS_CELL_RENDERER_CLASS(clz)       (G_TYPE_CHECK_CLASS_CAST ((clz),  TYPE_RECKLESS_CELL_RENDERER, RecklessCellRendererClass))
#define IS_CELL_PROGRESS_PROGRESS(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_RECKLESS_CELL_RENDERER))
#define IS_CELL_PROGRESS_PROGRESS_CLASS(clz)    (G_TYPE_CHECK_CLASS_TYPE ((clz),  TYPE_RECKLESS_CELL_RENDERER))
#define RECKLESS_CELL_RENDERER_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj),  TYPE_RECKLESS_CELL_RENDERER, RecklessCellRendererClass))

typedef struct _RecklessCellRenderer RecklessCellRenderer;
typedef struct _RecklessCellRendererClass RecklessCellRendererClass;

struct _RecklessCellRenderer {
    GtkCellRenderer parent;
    GtkWidget *cell;
};

struct _RecklessCellRendererClass {
    GtkCellRendererClass parent_class;
};

GType reckless_cell_renderer_get_type(void);

GObject* reckless_cell_renderer_new(void);

Impl:

#include "reckless_cell_renderer.h"

static void reckless_cell_renderer_init(RecklessCellRenderer *cell);

static void reckless_cell_renderer_class_init(RecklessCellRendererClass *clz);

static void reckless_cell_renderer_get_property(GObject *object, guint param_id,
        GValue *value, GParamSpec *pspec);

static void reckless_cell_renderer_set_property(GObject *object, guint param_id,
        const GValue *value, GParamSpec *pspec);

static void reckless_cell_renderer_finalize(GObject *gobject);

static void reckless_cell_renderer_get_size(GtkCellRenderer *cell,
        GtkWidget *widget, const GdkRectangle *cell_area, gint *x_offset,
        gint *y_offset, gint *width, gint *height);

static void reckless_cell_renderer_render(GtkCellRenderer *cell, cairo_t *ctx,
        GtkWidget *widget, const GdkRectangle *background_area,
        const GdkRectangle *cell_area, GtkCellRendererState state);

enum {
    PROP_CELL = 1,
};
static gpointer parent_class;

GType reckless_cell_renderer_get_type(void) {
    static GType cell__type = 0;

    if (cell__type)
        return cell__type;

    if (1) {
        static const GTypeInfo cell__info = { sizeof(RecklessCellRendererClass),
                NULL,
                NULL,
                (GClassInitFunc) reckless_cell_renderer_class_init, NULL,
                NULL,
                sizeof(RecklessCellRenderer), 0,
                (GInstanceInitFunc) reckless_cell_renderer_init, };

        cell__type = g_type_register_static(GTK_TYPE_CELL_RENDERER,
                "RecklessCellRenderer", &cell__info, 0);
    }

    return cell__type;
}

static void reckless_cell_renderer_init(RecklessCellRenderer *cellrenderer) {
}

static void reckless_cell_renderer_class_init(RecklessCellRendererClass *clz) {
    GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS(clz);
    GObjectClass *object_class = G_OBJECT_CLASS(clz);

    parent_class = g_type_class_peek_parent(clz);
    object_class->finalize = reckless_cell_renderer_finalize;

    object_class->get_property = reckless_cell_renderer_get_property;
    object_class->set_property = reckless_cell_renderer_set_property;

    cell_class->get_size = reckless_cell_renderer_get_size;
    cell_class->render = reckless_cell_renderer_render;

    g_object_class_install_property(object_class, PROP_CELL,
            g_param_spec_pointer("cell", "Cell", "Widget to display", G_PARAM_READWRITE));
}


static void reckless_cell_renderer_finalize(GObject *object) {
    /*
     RecklessCellRenderer *cellrenderer = RECKLESS_CELL_RENDERER(object);
     */

    (*G_OBJECT_CLASS(parent_class)->finalize)(object);
}

static void reckless_cell_renderer_get_property(GObject *object, guint param_id,
        GValue *value, GParamSpec *psec) {
    RecklessCellRenderer *cell = RECKLESS_CELL_RENDERER(object);

    switch (param_id) {
    case PROP_CELL:
        g_value_set_pointer(value, cell->cell);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, psec);
        break;
    }
}

static void reckless_cell_renderer_set_property(GObject *object, guint param_id,
        const GValue *value, GParamSpec *pspec) {
    RecklessCellRenderer *cell = RECKLESS_CELL_RENDERER(object);

    switch (param_id) {
    case PROP_CELL:
        cell->cell = g_value_get_pointer(value);
        break;

    default:
        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
        break;
    }
}

GObject*
reckless_cell_renderer_new(void) {
    return g_object_new(TYPE_RECKLESS_CELL_RENDERER, NULL);
}

static void reckless_cell_renderer_get_size(GtkCellRenderer *cell,
        GtkWidget *widget, const GdkRectangle *cell_area, gint *x_offset,
        gint *y_offset, gint *width, gint *height) {

    gint calc_width;
    gint calc_height;

    RecklessCellRenderer *rc = RECKLESS_CELL_RENDERER(cell);
    gtk_widget_get_size_request(rc->cell, &calc_width, &calc_height);


    if (width) {
        *width = calc_width;
    }

    if (height) {
        *height = calc_height;
    }

    if (cell_area) {
        if (x_offset) {
            *x_offset = (cell_area->width - *width);
            *x_offset = MAX(*x_offset, 0);
        }

        if (y_offset) {
            *y_offset = (cell_area->height - *height);
            *y_offset = MAX(*y_offset, 0);
        }
    }
}
static void reckless_cell_renderer_render(GtkCellRenderer *cell, cairo_t *ctx,
        GtkWidget *widget, const GdkRectangle *background_area,
        const GdkRectangle *cell_area, GtkCellRendererState state) {

    RecklessCellRenderer *rc = RECKLESS_CELL_RENDERER(cell);
    gtk_widget_size_allocate(rc->cell, cell_area);
    gtk_widget_draw(rc->cell, ctx);
}

尽管它不会崩溃,并且单元格指针始终有效(我在客户端代码中明确照顾了它),但未绘制项目.我完全确定我会错过一些非常重要的事情,例如小部件的寿命或绘制周期,但由于缺乏知识,我自己无法克服.

Though it does not crash, and the cell pointer is always valid (I explicitly look after it in the client code), the items are not drawn. I'm totally sure I miss something utterly important like the widget life or draw cycle, but cannot overcome it myself, due to the lack of knowledge.

我在这里想念什么?真的有可能吗?

What do I miss here? Is it actually a possible case at all?

推荐答案

令人惊讶的是,问题中的代码很好.对于代码来说,这些改变是必需的,该代码将"child"初始化为"child". (或更确切地说是"item",请参见下文)小部件.

Surprisingly, the code in the question is fine. The changes are required for the code, that initializes "child" (or rather "item", see below) widgets.

通常,当您使用普通的容器小部件并向其添加子级时,该容器会运行子级的初始化,这是完整功能(即绘图,信号发送等)所必需的.TreeView不是容器,因此其项目"在初始化时需要帮助.这是通过在每个子代上针对父代TreeView手动调用gtk_widget_set_parent()/gtk_widget_unparent()来完成的.但是,有一个陷阱.每次将父级TreeView锚定到窗口并从窗口取消锚定时,也应该调用gtk_widget_set_parent()/gtk_widget_unparent(),通常是通过

Normally, when you use the normal container widget, adding children to it, the container runs the initialization of a child, required for the full functionality, i.e. drawing, signaling etc. TreeView is not a container, and so its "items" require help on initialization. This is done by manually calling gtk_widget_set_parent()/gtk_widget_unparent() on each child against a parent TreeView. However, there is a catch. gtk_widget_set_parent()/gtk_widget_unparent() should be also called each time the parent TreeView is anchored to the window and deanchored from it, usually via listening to hierarchy-changed signal.

不幸的是,我使用的调用代码是用另一种语言编写的,该语言没有像C语言那样直接利用GTK API,所以我不能在这里提供它.尽管这很简单:收听hierarchy-changed信号,然后根据是否设置了根窗口,调用gtk_widget_set_parent(item_widget, tree_view)gtk_widget_unparent(item_widget),以及问题中的自定义单元格渲染器.最后但并非最不重要的一点是-项目小部件生命周期管理是您100%的责任,您必须手动检查所有悬空的指针.

Unfortunately, the calling code I use, is written in another language, which does not utilize GTK API directly, as in C, so I cannot provide it here. Though it is trivial: listen to the hierarchy-changed signal, and, depending on whether a root window is set or not, call gtk_widget_set_parent(item_widget, tree_view) or gtk_widget_unparent(item_widget), plus the custom cell renderer from the question. Last, but not least - item widgets lifecycle management is 100% your responsibility, you have to check all the dangling pointers manually.

小更新:在绘制过程中,Cairo上下文必须相应地移动到绘制坐标.因此,这是完整的抽奖程序:

Small update: in the draw procedure the Cairo context has to be shifted accordingly to the draw coordinates. So here is the full draw procedure:

static void reckless_cell_renderer_render(GtkCellRenderer *cell, cairo_t *ctx,
        GtkWidget *widget, const GdkRectangle *background_area,
        const GdkRectangle *cell_area, GtkCellRendererState state) {
    GdkRectangle allo;

    cairo_save(ctx);

    allo.x = cell_area->x;
    allo.y = cell_area->y;
    allo.width = cell_area->width;
    allo.height = cell_area->height;

    RecklessCellRenderer *rc = RECKLESS_CELL_RENDERER(cell);
    gtk_widget_size_allocate(rc->cell, &allo);
    cairo_translate(ctx, allo.x, allo.y);
    gtk_widget_draw(rc->cell, ctx);

    cairo_restore(ctx);
}

这篇关于GTK3自定义单元格渲染器,输出小部件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆