<-上一篇(GTK事件绑定简单解释)

众所周知,对于一个窗体来说,布局是非常重要的。就像Java Swing的各种layer,Gtk中也有各种布局。接下来我们就逐一说明

总览

名称

作用

GtkFixed

绝对定位

GtkBox

水平或垂直布局

GtkTable

表格布局

GtkFlowBox

类似于 HTML 的 flexbox 布局,自动排列子控件

GtkStack

堆叠多个控件,只显示一个控件

GtkListBox

垂直排列子控件,支持复杂的列表项

GtkPaned

分割窗口,可以调整子控件的大小

GtkScrolledWindow

滚动窗口,包含一个可以滚动的子控件

在开始之前。本文的主要代码如下:

#include <libadwaita-1/adwaita.h>
static void activate_cb (GtkApplication *app) {
    
}

int main (int argc, char *argv[]) {
    g_autoptr (AdwApplication) app = adw_application_new ("cn.hauchet.wangling", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate_cb), NULL);
    return g_application_run (G_APPLICATION (app), argc, argv);
}

主要修改点在activate_cb 函数,故后文只展示该函数中的代码修改

GtkFixed

首先是GtkFixed ,这个就有点类似于绝对定位,需要我们手动定位每个控件。示例代码如下:

GtkWidget *window = gtk_application_window_new (app);
GtkWidget *button = gtk_button_new_with_label ("Button");
GtkWidget *label = gtk_label_new ("Hello World");
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *fixed = gtk_fixed_new();
gtk_fixed_put(GTK_FIXED(fixed), label, 50, 50);
gtk_fixed_put(GTK_FIXED(fixed), button, 50, 100);
gtk_window_set_child (GTK_WINDOW (window), fixed);

gtk_window_present (GTK_WINDOW (window));

在这里,我们将label放在(50,50)这个位置,button放在(50,100)这个位置。需要注意的是,GTK中使用布局需要新建一个GtkWidget ,例如这里就是

GtkWidget *fixed = gtk_fixed_new();

GtkFixed中使用gtk_fixed_put 参数将控件加入布局中。最后别忘了gtk_window_set_child (GTK_WINDOW (window), fixed);GtkFixed加入到window中。

效果展示:

GtkBox

接下来是GtkBox ,也就是垂直或水平布局。使用这个布局前,我们同样需要新建一个GtkWidget

GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);

这里一共有两个参数,第一个参数指定了布局的方向,可选的值有GTK_ORIENTATION_HORIZONTALGTK_ORIENTATION_VERTICAL 这两个分别表示了水平和垂直方向。第二个参数表示控件之间的间距。

GtkBox 中想要添加控件,需要使用gtk_box_append 函数,添加顺序决定了控件显示顺序。

示例代码:

GtkWidget *window = gtk_application_window_new (app);
GtkWidget *button = gtk_button_new_with_label ("Button");
GtkWidget *label = gtk_label_new ("Hello World");
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 10);
gtk_box_append (GTK_BOX (vbox), label);
gtk_box_append (GTK_BOX (vbox), button);
gtk_window_set_child (GTK_WINDOW (window), vbox);

gtk_window_present (GTK_WINDOW (window));

效果展示:

GtkTable

GtkTable ,顾名思义,是一个表格布局。在这个布局中使用下面这个语句来创建新的布局。

GtkWidget *table = gtk_grid_new();

在这个布局中,想要添加控件,需要使用到gtk_grid_attach函数。

gtk_grid_attach(GTK_GRID(table), button1, 0, 0, 1, 1);

在这个示例代码中,有6个参数,分别是表格布局,需要添加的控件,列,行,列的跨度,行的跨度。

示例代码:

GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *table = gtk_grid_new();
GtkWidget *button1 = gtk_button_new_with_label ("Button 1");
GtkWidget *button2 = gtk_button_new_with_label ("Button 2");
GtkWidget *button3 = gtk_button_new_with_label ("Button 3");
GtkWidget *button4 = gtk_button_new_with_label ("Button 4");
GtkWidget *button5 = gtk_button_new_with_label ("Button 5");

gtk_grid_attach(GTK_GRID(table), button1, 0, 0, 1, 1);
gtk_grid_attach(GTK_GRID(table), button2, 1, 0, 1, 1);
gtk_grid_attach(GTK_GRID(table), button3, 0, 1, 1, 1);
gtk_grid_attach(GTK_GRID(table), button4, 1, 1, 1, 1);
gtk_grid_attach(GTK_GRID(table), button5, 0, 2, 2, 1);

gtk_window_set_child (GTK_WINDOW (window), table);

gtk_window_present (GTK_WINDOW (window));

效果展示:

GtkFlowBox

GtkFlowBox 是一种可以自动换行的布局。在这个布局中,使用以下语句新建布局:

GtkWidget *flowbox = gtk_flow_box_new();

想要添加控件,使用函数gtk_flow_box_insert ,例如:

gtk_flow_box_insert(GTK_FLOW_BOX(flowbox), button, -1);

一共有三个参数,第一个参数指定了flowbox,第二个参数指定了需要添加的控件,第三个参数是插入的位置,-1表示插入到最后。

示例代码:

GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *flowbox = gtk_flow_box_new();
gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(flowbox), 3);
gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(flowbox), GTK_SELECTION_NONE);

for (int i = 1; i <= 10; i++) {
    char button_label[20];
    snprintf(button_label, sizeof(button_label), "Button %d", i);
    GtkWidget *button = gtk_button_new_with_label(button_label);
    gtk_flow_box_insert(GTK_FLOW_BOX(flowbox), button, -1);
}

gtk_window_set_child (GTK_WINDOW (window), flowbox);
gtk_window_present (GTK_WINDOW (window));

效果展示:

可以看到,控件位置跟随窗体大小发生了变化。

GtkStack

这个布局无论添加多少个控件,都只会显示一个,代码如下:

GtkWidget *stack = gtk_stack_new();

使用gtk_stack_add_titled 函数添加控件,参数有四个,分别指的是stack,控件,控件的name,控件的title

gtk_stack_add_titled(GTK_STACK(stack), button, button_label, button_label);

示例代码:

GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *stack = gtk_stack_new();
gtk_stack_set_transition_type(GTK_STACK(stack), GTK_STACK_TRANSITION_TYPE_SLIDE_LEFT_RIGHT);
gtk_stack_set_transition_duration(GTK_STACK(stack), 1000);

for (int i = 1; i <= 10; i++) {
    char button_label[20];
    snprintf(button_label, sizeof(button_label), "Button %d", i);
    GtkWidget *button = gtk_button_new_with_label(button_label);
    gtk_stack_add_titled(GTK_STACK(stack), button, button_label, button_label);
}

gtk_window_set_child (GTK_WINDOW (window), stack);
gtk_window_present (GTK_WINDOW (window));

效果展示:

GtkListBox

这个控件相当于一个列表,使用如下语句新建。

GtkWidget *listbox = gtk_list_box_new();

使用gtk_list_box_row_set_childgtk_list_box_append 添加控件,例如:

gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), button);
gtk_list_box_append(GTK_LIST_BOX(listbox), row);

gtk_list_box_row_set_child 有两个参数,第一个参数指的是行,第二个参数指的是控件。gtk_list_box_append 也有两个参数,第一个参数指的是listbox,第二个参数指的是行。

示例代码:

GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *listbox = gtk_list_box_new();

for (int i = 1; i <= 10; i++) {
    char button_label[20];
    snprintf(button_label, sizeof(button_label), "Button %d", i);
    GtkWidget *button = gtk_button_new_with_label(button_label);
    GtkWidget *row = gtk_list_box_row_new();
    gtk_list_box_row_set_child(GTK_LIST_BOX_ROW(row), button);
    gtk_list_box_append(GTK_LIST_BOX(listbox), row);
}

gtk_window_set_child (GTK_WINDOW (window), listbox);
gtk_window_present (GTK_WINDOW (window));

效果展示:

GtkPaned

这是一个从中间将左右或上下两边分开的布局,可以自由调节大小,新建布局的语句是:

GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);

这里有一个参数,分别可以是GTK_ORIENTATION_HORIZONTALGTK_ORIENTATION_VERTICAL

这里我们同样新建两个GtkBox 用于存放按钮:

GtkWidget *box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
GtkWidget *box2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

GtkPaned添加控件的语句是gtk_paned_set_start_child

示例代码:

GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *paned = gtk_paned_new(GTK_ORIENTATION_HORIZONTAL);
GtkWidget *box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);
GtkWidget *box2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

for (int i = 1; i <= 5; i++) {
    char button_label[20];
    snprintf(button_label, sizeof(button_label), "Button %d", i);
    GtkWidget *button = gtk_button_new_with_label(button_label);
    gtk_box_append(GTK_BOX(box1), button);
}

for (int i = 6; i <= 10; i++) {
    char button_label[20];
    snprintf(button_label, sizeof(button_label), "Button %d", i);
    GtkWidget *button = gtk_button_new_with_label(button_label);
    gtk_box_append(GTK_BOX(box2), button);
}

gtk_paned_set_start_child(GTK_PANED(paned), box1);
gtk_paned_set_end_child(GTK_PANED(paned), box2);

gtk_window_set_child (GTK_WINDOW (window), paned);
gtk_window_present (GTK_WINDOW (window));

效果展示:

GtkScrolledWindow

使用这个布局的窗口,当内容超出窗口大小时,会自动出现滚动条。新建的语句为

GtkWidget *scrolled_window = gtk_scrolled_window_new();

这里同样混合使用GtkBox ,用于存放按钮。

添加控件使用gtk_scrolled_window_set_child .

gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window), box);

示例代码:

GtkWidget *window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "Hello");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);

GtkWidget *scrolled_window = gtk_scrolled_window_new();
GtkWidget *box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 5);

for (int i = 1; i <= 10; i++) {
    char button_label[20];
    snprintf(button_label, sizeof(button_label), "Button %d", i);
    GtkWidget *button = gtk_button_new_with_label(button_label);
    gtk_box_append(GTK_BOX(box), button);
}

gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(scrolled_window), box);
gtk_window_set_child(GTK_WINDOW(window), scrolled_window);
gtk_window_present(GTK_WINDOW(window));

效果展示:

那么这篇文章就写到这里,更多内容敬请期待。