GTK3 custom cell renderer, that outputs widgets

ⅰ亾dé卋堺 提交于 2020-07-10 07:52:21

问题


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

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.

Header:

#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?


回答1:


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

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.

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.

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


来源:https://stackoverflow.com/questions/62258691/gtk3-custom-cell-renderer-that-outputs-widgets

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!