如何为 GtkTreeView 单元格添加悬停效果

How to add a hover effect to a GtkTreeView cell

我有一个 GtkTreeView,其中包含两列文本类型(例如 G_TYPE_STRING),我正在使用 GtkCellRendererText 来呈现该列。

当鼠标进入和离开某个单元格然后悬停或突出显示该单元格时,为什么我可以做出反应。

例如,当鼠标进入单元格渲染器时,我想在文本中加下划线,以便提供视觉线索,表明可以单击单元格以执行操作。

如果存在 PRELIT 标志,您可以制作自定义 CellRendererText 并设置下划线 属性。

我只知道Python:

class My_CellRendererText( Gtk.CellRendererText ):
def __init__( self ):
    super().__init__()

def do_render( self, cr, widget, background_area, cell_area, flags ):
    self.props.underline = ( ( flags & Gtk.CellRendererState.PRELIT ) != 0 )
    return Gtk.CellRendererText.do_render( self, cr, widget, background_area, cell_area, flags )

GObject.type_register( My_CellRendererText )

完成任务的方法有很多。下面是几个示例,由水平线分隔。


最简单,在某些方面也是最好的选择是在 GtkTreeView 中简单地将鼠标光标更改为手指(与您在浏览器中指向 link 时看到的鼠标光标相同) .

假设GtkTreeView *view是我们感兴趣的视图。你调用了gtk_widget_show_all(window);之后,你可以调用

GdkWindow  *win = gtk_tree_view_get_bin_window(GTK_TREE_VIEW(view));
GdkDisplay *disp = gdk_window_get_display(win);
gdk_window_set_cursor(win, gdk_cursor_new_from_name(disp, "pointer"));

有了这个,鼠标指针将成为视图中所有行的手指,不包括 header 行。

您可以使用的一组游标名称(而不是上面的 "pointer")在 GDK3 文档中列出 here

不利的是,如果树视图中有空行或分隔行,鼠标指针看起来仍然像手指。


您可以通过应用 application-specific CSS 悬停颜色来覆盖悬停颜色,比如

GtkTreeView:hover {
    color: #ff0000;
}

例如

GtkCssProvider *css_provider;
css_provider = gtk_css_provider_new();
if (gtk_css_provider_load_from_data(css_provider, 
            "GtkTreeView:hover {\n"
            "    color: #ff0000;\n"
            "}\n"
            "", -1, NULL))
    gtk_style_context_add_provider_for_screen(gdk_screen_get_default(),
                GTK_STYLE_PROVIDER(css_provider),
                GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref(css_provider);

创建主 window 之后。 (当然,您可以在 CSS 片段中包含所有 application-specific CSS 覆盖。)

但是,至少在 gtk+ 3.18.9 上,渲染器似乎不支持此处的 text-decorationfont CSS 属性。 (如果您想知道,GtkCssProvider 似乎根本不支持 cursor 属性。)

此外,由于这基本上覆盖了用户主题,并且 Gtk+ 用户可能正在使用许多可能的主题,因此以这种方式更改颜色不太可能在所有主题中看起来都很好。


如果您的 GtkTreeView *view; 由可点击的行组成,并且您不需要选择任何内容(也就是说,您没有单独的按钮或导致对所有行应用操作的东西GtkTreeView 中选定的行),那么您可以简单地将选择设置为跟随鼠标光标:

gtk_tree_view_set_hover_selection(view, TRUE);

这会选中您悬停的行,因此也会突出显示。您可能还想一次将选择限制为一行:

gtk_tree_selection_set_mode(gtk_tree_view_get_selection(view),
                            GTK_SELECTION_SINGLE);

Herbalist already mentioned一样,您可以创建一个自定义的 GtkCellRendererText 导数,它会根据正在呈现的文本单元格是否也预亮(悬停)来更改 PangoUnderline underline 属性 :

#include <gtk/gtk.h>

#define CUSTOM_TYPE_CELL_RENDERER_TEXT           (custom_cell_renderer_text_get_type())
#define CUSTOM_CELL_RENDERER_TEXT(obj)           (G_TYPE_CHECK_INSTANCE_CAST((obj), CUSTOM_TYPE_CELL_RENDERER_TEXT, CustomCellRendererText))
#define CUSTOM_CELL_RENDERER_TEXT_CLASS(cls)     (G_TYPE_CHECK_CLASS_CAST((cls),    CUSTOM_TYPE_CELL_RENDERER_TEXT, CustomCellRendererTextClass))
#define CUSTOM_IS_CELL_RENDERER_TEXT(obj)        (G_TYPE_CHECK_INSTANCE_TYPE((obj), CUSTOM_TYPE_CELL_RENDERER_TEXT))
#define CUSTOM_IS_CELL_RENDERER_TEXT_CLASS(cls)  (G_TYPE_CHECK_CLASS_TYPE((cls),    CUSTOM_TYPE_CELL_RENDERER_TEXT))
#define CUSTOM_CELL_RENDERER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj),  CUSTOM_TYPE_CELL_RENDERER_TEXT, CustomCellRendererTextClass))

GType  custom_cell_renderer_text_get_type(void) G_GNUC_CONST;

typedef struct {
    GtkCellRendererText  renderer;
} CustomCellRendererText;

typedef struct {
    GtkCellRendererTextClass  parent_class;
} CustomCellRendererTextClass;

G_DEFINE_TYPE(CustomCellRendererText, custom_cell_renderer_text, GTK_TYPE_CELL_RENDERER_TEXT);

void custom_cell_renderer_text_render(GtkCellRenderer *cell,
                                      cairo_t *cr,
                                      GtkWidget *widget,
                                      const GdkRectangle *backarea,
                                      const GdkRectangle *cellarea,
                                      GtkCellRendererState state)
{
    if (state & GTK_CELL_RENDERER_PRELIT) {
        const PangoUnderline prelit = PANGO_UNDERLINE_SINGLE;
        PangoUnderline curr;
        g_object_get(cell, "underline", &curr, NULL);
        g_object_set(cell, "underline", prelit, NULL);
        ((GtkCellRendererClass *)custom_cell_renderer_text_parent_class)->render(cell, cr, widget, backarea, cellarea, state);
        g_object_set(cell, "underline", curr, NULL);
    } else
        ((GtkCellRendererClass *)custom_cell_renderer_text_parent_class)->render(cell, cr, widget, backarea, cellarea, state);
}

void custom_cell_renderer_text_class_init(CustomCellRendererTextClass *cls)
{
    ((GtkCellRendererClass *)cls)->render = custom_cell_renderer_text_render;
    return;
}

void custom_cell_renderer_text_init(CustomCellRendererText *renderer)
{
    return;
}

GtkCellRenderer *custom_cell_renderer_text_new(void)
{
    return g_object_new(CUSTOM_TYPE_CELL_RENDERER_TEXT, NULL);
}

如果您随后在 GtkTreeView 代码中将 gtk_cell_render_text_new() 替换为 custom_cell_render_text_new()(并将 GtkCellRenderText * 更改为 CustomCellRenderText *),则您在 GtkTreeView 中悬停的行将变成下划线。

请注意,您悬停的行中的所有单元格都将突出显示,而不仅仅是您悬停的 cell/column。这是因为在 GtkTreeView 中,行(或索引)是被操作的单位(而不是特定列上的特定单元格)。

如果您仅对 GtkTreeView 中的某些列使用上述渲染器,即使鼠标悬停在该行的其他列上,这些列中的文本也会带有下划线。

另请注意,如果您不希望所有行都指示可点击性,您可以在数据模型中添加第三列 -- name "sensitive",为 gboolean 类型键入 G_TYPE_BOOLEAN;值 TRUE 如果该行是可点击的,FALSE 如果该行是 non-clickable -- 并将 custom_cell_renderer_text_render() 中的 if 子句更改为

    const GtkStateFlags  flags = gtk_cell_renderer_get_state(cell, widget, state);
    if ((state & GTK_CELL_RENDERER_PRELIT) &&
       !(flags & GTK_STATE_FLAG_INSENSITIVE)) {

Gtk+ 将正常呈现带有 TRUE 的行,以及带有 FALSE 的行作为分隔符或禁用文本,因为模型中的列名自动与同名的 GtkCellRenderer 属性相关联。