记一次Gtk编程遇到问题的解决过程 (GtkSearchEntry绑定新的快捷键,信号发生快捷键)

最近用到GtkSearchEntry的编程,其中一个信号是stop-search,对应绑定的默认快捷键是ESC,一般捕获为退出信号,感觉不顺手,想绑定到Ctrl-C,中途遇到了问题,查看全网没有发现解决方案,但是从StackOverFlow的这篇文章里找到一点灵感 Gtk3 keys bindings in css files。特此记录此问题的解决过程和思路。

查看Gtk开发手册,如下:

The  stop-search signal

The ::stop-search signal is a keybinding signal which gets emitted when the user stops a search via keyboard input.

Applications should connect to it, to implement hiding the search entry in this case.

The default bindings for this signal is Escape.

Parameters

entry the entry on which the signal was emitted
user_data user data set when the signal handler was connected.

Flags: Action

Since: 3.16

通过描述可以看到stop-search是键盘绑定信号,用户输入特定键盘触发后发射出该信号。

点进 keybinding signal ,找到文章开头的一些函数:

void gtk_binding_entry_add_signall ()
GtkBindingSet * gtk_binding_set_new ()
GtkBindingSet * gtk_binding_set_by_class ()
GtkBindingSet * gtk_binding_set_find ()
gboolean gtk_bindings_activate ()
gboolean gtk_bindings_activate_event ()
gboolean gtk_binding_set_activate ()
void gtk_binding_entry_add_signal ()
GTokenType gtk_binding_entry_add_signal_from_string ()
void gtk_binding_entry_skip ()
void gtk_binding_entry_remove ()
void gtk_binding_set_add_path ()

其中看着比较像的是gtk_binding_entry_add_signal (),点进去看函数原型:

gtk_binding_entry_add_signal ()

Override or install a new key binding for  keyval with  modifiers on  binding_set . When the binding is activated,  signal_name will be emitted on the target widget, with  n_args  Varargs used as arguments.

Each argument to the signal must be passed as a pair of varargs: the GType of the argument, followed by the argument value (which must be of the given type). There must be  n_args pairs in total.

 

keyval(键值),modifiers(修饰符),和signal_name(信号名)容易知道,比如我想要绑定到Ctrl-C,那么keyval就是c,modifiers就是control,signal-name就是stop-search,但是写法可能要替换成一些特定的宏,这些问题不大,从gtk的源码中找到规律,keyval一般写成 GDK_KEY_c 这种格式,Control写成 GDK_CONTROL_MASK , signal-name还是原来的字符串不用变。

问题最大的是GtkBindingSet如何获取,它的原型是一个结构体,里面包含很多变量,我们不可能自己定义一个,因为这在Gtk源码中肯定已经定义过了,目的到现在变成了如何获取这个GtkBindingSet。

再细看上面的一些函数,其中有3个返回值都是GtkBindingSet,大概率是从这里入手,通过名称可以排除包含new的函数,因为是获取已有的,而不是new一个新的,剩下两个通过对比确定为如下目标函数:

gtk_binding_set_by_class ()

This function returns the binding set named after the type name of the passed in class structure. New binding sets are created on demand by this function.

Parameters

object_class a valid GObject class

Returns

the binding set corresponding to  object_class .

至于为什么选这个,首先从它的内部传参可以看出,只需要传进一个有效的GObject class 即可。

因此,问题再进一步,变成:如何获取GObject class

到这里,感觉离目标已经越来越接近,但是对于Gtk Class了解不够,且不太懂得该如何获取,其内部机制也不了解。

一个很关键的思路转折出现了,既然GtkSearchEntry是继承自GtkEntry,那么关键突破点不应该只在GtkSearchEntry中找,很有可能在其继承的基类中存在一些我们要的结果。

 

而类名一般与部件widget名相关,即,可能对我们有帮助的类名是GtkSearchEntryClass或者是GtkEntryClass,接着我在Gtk源码根目录输入如下命令:

从输出结果中看到这么一行

用vim打开这个文件:找到上面的内容,如下:

发现使用 GTK_ENTRY_GET_CLASS()宏即可返回GtkEntry的类,而且由GtkSearchEntry继承自GtkEntry的关系中可以推断,这个宏对GtkSearchEntry应该同样起作用,由此又更进一步,得到了GtkEntryClass,最后一步只要获取到GObjectClass就真正搞定了

接下来又在gtksearchentry.c中找到以下语句:

同样是采用宏定义的方式将某个Class转换成了原始的GObjectClass,到此追根溯源就此结束,全部未知变量我们都已经拿到,可以进行快捷键注册了。

直接上代码:

命名searchWin.c

编译:

运行:

程序现象:

弹出一个搜索框,输入内容会实时被打印到终端,按Ctrl-C退出界面。

 

拓展:

完成后突然想到都是Class,GObjectClass应该可以从其他Class直接强转过来,试了后发现果然,直接将 GObjectClass *class = (GObjectClass*)( klass ) 代替 GObjectClass *class = G_OBJECT_CLASS ( klass );效果是一样的,宏定义完成的方式猜测也包含强制转换。

 

结语:

要善于利用互联网的力量,但也要培养起自己独立解决问题的能力和思路,方能解前人所未解之难题,在编程的路上越走越远,成为一名创造者,而非搬砖者。