自定义组件
鉴于网上关于gtk4的文章很少,有关自定义组件的文章更是少的可怜,有好多都不能用了。所以写此文章以做记录。写此文章时,使用的gtk版本为4.8.3。
自定义组件步骤
1、定义控件的类型
定义控件类型有两种方式,
(1)、直接在c文件中定义类型;
(2)、先在头文件中使用G_DECLARE_FINAL_TYPE宏声明一个最终类型的结构体和类结构体。
1.1、直接在c文件中定义类型
typedef struct
{
GtkBox parent_instance;
} MyWidget;
typedef struct
{
GtkBoxClass klass;
} MyWidgetClass;
// 使用**G_DEFINE_TYPE**宏定义新控件的类型。这个宏会生成必要的类型定义和初始化函数。
G_DEFINE_TYPE(MyWidget, my_widget, GTK_TYPE_BOX);
1.2、在头文件中使用G_DECLARE_FINAL_TYPE宏声明一个最终类型的结构体和类结构体。
// .h文件
G_DECLARE_FINAL_TYPE(MyWidget, my_widget, MY, WIDGET, GtkBox)
// .c文件
struct _MyWidget
{
GtkBox parent_instance;
};
struct _MyWidgetClass
{
GtkBoxClass klass;
} ;
// 使用**G_DEFINE_TYPE**宏定义新控件的类型。这个宏会生成必要的类型定义和初始化函数。
G_DEFINE_TYPE(MyWidget, my_widget, GTK_TYPE_BOX);
2、实现初始化函数
由上步G_DEFINE_TYPE宏定义好一个MyWidget控件,该宏内部定义和实现了一系列方法(方法命名规则:控件名称_方法名称,如:my_widget_init),我们只需要重写我们需要的方法即可。以下只列举其中几个。
// 实例初始化函数,可以设置自定义控件的实例属性。
static void my_widget_init(MyWidget *self)
{
}
// 类初始化函数,可以设置自定义控件的类属性和方法。
static void my_widget_class_init(MyWidgetClass *klass)
{
}
3、创建自定义控件的实例化函数
使用g_object_new创建控件的实例。在创建之前需要定义一个宏,该宏指的是自定义控件的类型。
// MY_WIDGET_TYPE 自定义宏
// my_widget_get_type() G_DEFINE_TYPE 内部实现的一个方法
#define MY_WIDGET_TYPE my_widget_get_type()
GtkWidget *my_widget_new(void)
{
return g_object_new(MY_WIDGET_TYPE, NULL);
}
4、创建一个控件的实例,并将其添加到其他控件中。例如,将其添加到GtkWindow中
GtkWindow *win = GTK_WINDOW(gtk_builder_get_object(builder, "win"));
gtk_application_add_window(app, win);
// 创建一个控件实例
GtkWidget *myWidget = my_widget_new();
// 添加到窗口中
gtk_window_set_child(win, myWidget);
//
gtk_window_present(win);
5、向自定义组件中填充内容
- 重写snapshot方法
gtk4中没有draw方法,取而代之的是snapshot方法,当需要重写绘制逻辑的时候就需要重写它。
//
static void on_override_snapshot(GtkWidget *widget, GtkSnapshot *snapshot)
{
gtk_snapshot_append_color(snapshot, &(GdkRGBA){1.0, 0.0, 0.0, 1.0}, &GRAPHENE_RECT_INIT(100, 100, 100, 100));
gtk_snapshot_save(snapshot);
}
//
static void my_widget_class_init(MyWidgetClass *klass)
{
GtkWidgetClass *class = GTK_WIDGET_CLASS(klass);
class->snapshot = on_override_snapshot;
}
- 添加其他现有的控件
//
static void my_widget_init(MyWidget *self)
{
GtkWidget *button = gtk_button_new_with_label("Button");
gtk_box_append(GTK_BOX(self), button);
gtk_widget_set_size_request(button, 400, 40);
}
//
static void my_widget_class_init(MyWidgetClass *klass)
{
}
6、在UI文件中加载自定义组件
要想在ui文件中加载自定义的组件,需要先注册自定义组件(一般在加载ui文件之前注册),注册的方法有两种:g_type_register_static和g_type_ensure
6.1、使用g_type_ensure宏注册组件
g_type_ensure(MY_WIDGET_TYPE);
6.2、使用g_type_register_static函数注册组件
const GTypeInfo widget_info = {
sizeof(MyWidgetClass),
NULL, // base_init
NULL, // base_finalize
(GClassInitFunc)my_widget_class_init,
NULL, // class_finalize
NULL, // class_data
sizeof(MyWidget),
0, // n_preallocs
(GInstanceInitFunc)my_widget_init,
NULL // value_table
};
g_type_register_static(GTK_TYPE_BOX, "MyWidget", &widget_info, 0);