Глава 17: Гибкая Компоновка с GtkGrid ===================================== В предыдущей главе мы освоили линейную компоновку с `GtkBox`, которая идеально подходит для размещения виджетов по горизонтали или вертикали. Однако для более сложных, табличных или многомерных макетов нам потребуется более мощный инструмент. `GtkGrid` позволяет располагать виджеты в виде таблицы с заданным количеством строк и столбцов. Это даёт разработчику полную свободу и точный контроль над позиционированием элементов, а также возможность объединять ячейки, создавая гибкие и адаптивные интерфейсы. ### Что вы узнаете в этой главе: * Как создать и инициализировать контейнер `GtkGrid`. * Как добавлять виджеты в конкретные ячейки сетки, используя координаты (строка, столбец). * Как объединять ячейки по горизонтали (`column-span`) и вертикали (`row-span`). * Как управлять отступами между элементами сетки и вокруг неё. * Как использовать параметры расширения (`expand`) для адаптивного дизайна. --- Основные Возможности GtkGrid ---------------------------- `GtkGrid` предоставляет гораздо больший контроль над расположением элементов по сравнению с `GtkBox`: * **Размещение элементов в ячейках (row, column):** Каждый виджет помещается в определённую ячейку сетки, заданную координатами строки и столбца, начиная с (0,0) для левого верхнего угла. * **Объединение ячеек (row-span, column-span):** Виджет может занимать не одну ячейку, а растягиваться на несколько строк или столбцов, что идеально подходит для создания сложных макетов (например, заголовок, занимающий всю ширину, или боковая панель, занимающая несколько строк). * **Установка отступов:** Можно задавать отступы как между строками и столбцами, так и вокруг всей сетки. * **Растягивание/масштабирование элементов:** С помощью свойств расширения (`expand`) и заполнения (`fill`), а также гомогенности, можно контролировать, как виджеты и сама сетка реагируют на изменение размера окна. --- ### Пример 17.1: Использование GtkGrid для размещения кнопок в виде таблицы Название исходного файла: `grid_example.c` Этот пример демонстрирует базовое использование `GtkGrid` для создания простого калькулятороподобного макета с кнопками, включая объединение ячеек. .. code-block:: c #include // Подключаем основную библиотеку GTK. /** * @brief Callback-функция, которая будет вызвана при нажатии любой кнопки. * * Эта функция извлекает метку с нажатой кнопки и выводит её в консоль. * Это полезно для демонстрации, какая именно кнопка была активирована. * * @param widget Указатель на GtkWidget, который испустил сигнал (GtkButton). * @param data Универсальный указатель на пользовательские данные (в данном случае NULL). */ static void on_button_clicked(GtkWidget *widget, gpointer data) { const char *button_label = gtk_button_get_label(GTK_BUTTON(widget)); g_print("Нажата кнопка: %s\n", button_label); } /** * @brief Функция активации приложения. * * Эта функция вызывается, когда приложение запускается и готово к отображению * своих окон. Здесь создаются главное окно, GtkGrid и все виджеты. * * @param app Указатель на объект GtkApplication. * @param user_data Универсальный указатель на пользовательские данные, * переданные при подключении сигнала "activate". В данном случае NULL. */ static void activate(GtkApplication *app, gpointer user_data) { GtkWidget *window; GtkWidget *grid; GtkWidget *button1, *button2, *button3, *button4; // Объявляем все кнопки // 1. Создание главного окна приложения. // gtk_application_window_new(app) создает окно, связанное с GtkApplication, // что позволяет GTK лучше управлять жизненным циклом приложения. window = gtk_application_window_new(app); gtk_window_set_title(GTK_WINDOW(window), "GtkGrid Пример"); gtk_window_set_default_size(GTK_WINDOW(window), 300, 200); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER); // 2. Создание контейнера GtkGrid. // GtkGrid - это контейнер, который позволяет располагать виджеты // в виде таблицы (по строкам и столбцам). grid = gtk_grid_new(); // 3. Настройка GtkGrid. // Установка внешнего отступа (границы) вокруг всей сетки в 10 пикселей. gtk_container_set_border_width(GTK_CONTAINER(grid), 10); // Установка отступа в 5 пикселей между строками. gtk_grid_set_row_spacing(GTK_GRID(grid), 5); // Установка отступа в 5 пикселей между столбцами. gtk_grid_set_column_spacing(GTK_GRID(grid), 5); // Можно также сделать строки/столбцы гомогенными (равными по размеру) // gtk_grid_set_row_homogeneous(GTK_GRID(grid), TRUE); // gtk_grid_set_column_homogeneous(GTK_GRID(grid), TRUE); // 4. Создание кнопок, которые будут размещены в сетке. button1 = gtk_button_new_with_label("Кнопка 1"); button2 = gtk_button_new_with_label("Кнопка 2"); button3 = gtk_button_new_with_label("Кнопка 3 (объединена)"); button4 = gtk_button_new_with_label("Кнопка 4"); // 5. Подключение обработчика кликов для каждой кнопки. g_signal_connect(button1, "clicked", G_CALLBACK(on_button_clicked), NULL); g_signal_connect(button2, "clicked", G_CALLBACK(on_button_clicked), NULL); g_signal_connect(button3, "clicked", G_CALLBACK(on_button_clicked), NULL); g_signal_connect(button4, "clicked", G_CALLBACK(on_button_clicked), NULL); // 6. Добавление кнопок в сетку с использованием gtk_grid_attach(). // Эта функция - ключевая для GtkGrid. Её параметры: // - GTK_GRID(grid): Указатель на GtkGrid. // - buttonN: Виджет, который добавляется. // - left: Индекс столбца, с которого начинается виджет (0-индексированный). // - top: Индекс строки, с которой начинается виджет (0-индексированный). // - width: Количество столбцов, которое занимает виджет (column-span). // - height: Количество строк, которое занимает виджет (row-span). // Кнопка 1: Столбец 0, Строка 0, занимает 1 столбец, 1 строку. gtk_grid_attach(GTK_GRID(grid), button1, 0, 0, 1, 1); // Кнопка 2: Столбец 1, Строка 0, занимает 1 столбец, 1 строку. gtk_grid_attach(GTK_GRID(grid), button2, 1, 0, 1, 1); // Кнопка 3: Столбец 0, Строка 1, занимает 2 столбца (растягивается), 1 строку. gtk_grid_attach(GTK_GRID(grid), button3, 0, 1, 2, 1); // Кнопка 4: Столбец 0, Строка 2, занимает 1 столбец, 1 строку. gtk_grid_attach(GTK_GRID(grid), button4, 0, 2, 1, 1); // 7. Добавление GtkGrid в окно. // GtkWindow может содержать только один дочерний виджет, поэтому мы добавляем GtkGrid. gtk_container_add(GTK_CONTAINER(window), grid); // 8. Отображение всех виджетов (окна, сетки и всех кнопок внутри). gtk_widget_show_all(window); } /** * @brief Главная функция программы, точка входа. * * Эта функция отвечает за создание объекта GtkApplication, * подключение функции активации и запуск основного цикла приложения. * * @param argc Количество аргументов командной строки. * @param argv Массив строк аргументов командной строки. * @return Код завершения программы. */ int main(int argc, char **argv) { GtkApplication *app; // Указатель на объект приложения GTK int status; // Переменная для хранения статуса завершения приложения // Создание нового объекта GtkApplication. // "org.example.GridApp" - уникальный идентификатор приложения (обычно в стиле DNS). // G_APPLICATION_FLAGS_NONE - стандартные флаги приложения. app = gtk_application_new("org.example.GridApp", G_APPLICATION_FLAGS_NONE); // Подключение сигнала "activate" к нашей функции 'activate'. // Сигнал "activate" испускается, когда приложение запускается и готово // создать свое первое окно или показать себя пользователю. g_signal_connect(app, "activate", G_CALLBACK(activate), NULL); // Запуск приложения. // g_application_run() запускает главный цикл событий GLib (и GTK). // Управление передается GTK, и приложение будет ждать событий. // Функция возвращает статус завершения, когда приложение закрывается. status = g_application_run(G_APPLICATION(app), argc, argv); // Освобождение ресурсов, связанных с объектом GtkApplication. g_object_unref(app); return status; // Возвращаем статус завершения. }