【问题标题】:GTK3 custom cell renderer, that outputs widgetsGTK3 自定义单元格渲染器,输出小部件
【发布时间】:2020-09-27 05:15:32
【问题描述】:

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

这些小部件的生命周期 100% 由我在父 TreeView 之外手动控制。不关心键盘导航和可访问性(到目前为止)。我现在得到的是修改后的官方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);

实施:

#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);
}

虽然它没有崩溃,并且单元格指针始终有效(我在客户端代码中明确地关注它),但项目没有被绘制。我完全确定我错过了一些非常重要的东西,比如小部件生命或绘制周期,但由于缺乏知识,我自己无法克服它。

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

【问题讨论】:

    标签: c gtk3 cellrenderer


    【解决方案1】:

    令人惊讶的是,问题中的代码很好。代码需要更改,初始化“子”(或者更确切地说“项目”,见下文)小部件。

    通常,当您使用普通容器小部件时,向其中添加子组件时,容器会运行子组件的初始化,这是完整功能所必需的,即绘图、信号发送等。TreeView 不是容器,因此它的“项目”在初始化时需要帮助。这是通过在每个孩子上手动调用gtk_widget_set_parent()/gtk_widget_unparent() 来对抗父母TreeView 来完成的。但是,有一个问题。 gtk_widget_set_parent()/gtk_widget_unparent() 也应该在每次父 TreeView 锚定到窗口并从窗口解除锚定时调用,通常通过 listening to hierarchy-changed signal。

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

    小更新:在绘图过程中,Cairo 上下文必须相应地移动到绘图坐标。所以这里是完整的绘制过程:

    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);
    }
    

    【讨论】:

      猜你喜欢
      • 2023-01-12
      • 1970-01-01
      • 2019-02-27
      • 1970-01-01
      • 2015-01-01
      • 2018-12-26
      • 2011-11-08
      • 2019-10-13
      相关资源
      最近更新 更多