GTK

GNOMEデスクトップ環境の基盤となるマルチプラットフォームGUIツールキット。オブジェクト指向設計とテーマシステムによる高いカスタマイズ性。アクセシビリティサポートと国際化機能が充実。

デスクトップクロスプラットフォームCGObjectLinuxUnixGUI

GitHub概要

GNOME/gtk

Read-only mirror of https://gitlab.gnome.org/GNOME/gtk

スター1,561
ウォッチ79
フォーク389
作成日:2012年6月3日
言語:C
ライセンス:Other

トピックス

なし

スター履歴

GNOME/gtk Star History
データ取得日時: 2025/7/15 22:54

フレームワーク

GTK

概要

GTKは、GNOMEデスクトップ環境の基盤となっているクロスプラットフォームGUIツールキットです。C言語で開発され、GObject型システムに基づいて構築されており、Linux/Unix系システムでのデスクトップアプリケーション開発において標準的な位置を占めています。

詳細

GTKは2025年現在、GTK 4が安定版として提供されており、30年以上の歴史を持つ成熟したGUIフレームワークです。GNOME、XFCE、LXDEなどの主要なLinuxデスクトップ環境で使用され、多くのオープンソースアプリケーションの基盤として採用されています。

GTKの主要な特徴:

  • ネイティブパフォーマンス: C言語による高性能なネイティブ実行
  • 豊富なウィジェット: 包括的なGUIコンポーネントセット
  • テーマ対応: CSS-likeスタイルシステムによる柔軟なカスタマイズ
  • アクセシビリティ: AT-SPIによる優秀なアクセシビリティサポート
  • 国際化: 完全なi18n/l10nサポート
  • クロスプラットフォーム: Linux、Windows、macOSでの動作

2025年現在、Firefox、GIMP、LibreOffice(一部)、VSCodeなど、多くの主要アプリケーションがGTKを採用しています。

メリット・デメリット

メリット

  • Linux統合: Linux/UNIXシステムでの完全なネイティブ体験
  • 高性能: C言語による最適化されたパフォーマンス
  • 安定性: 30年以上の実績による高い安定性
  • 豊富な機能: 包括的なウィジェットセットとレイアウトマネージャー
  • フリーソフトウェア: LGPLライセンスによる自由な利用
  • コミュニティ: 大規模で活発な開発コミュニティ
  • ドキュメント: 豊富な公式ドキュメントとチュートリアル
  • バインディング: Python、Rust、Vala等の多言語バインディング

デメリット

  • 学習曲線: C言語とGObjectシステムの理解が必要
  • Windows/macOS体験: ネイティブルック&フィールが制限される
  • 開発ツール: IDEサポートがQt等と比較して限定的
  • パッケージング: 各プラットフォームでの配布が複雑
  • モダンUI: 最新のUIデザイントレンドへの対応が限定的
  • メモリ使用量: GObjectシステムによるオーバーヘッド

主要リンク

書き方の例

基本的なGTKアプリケーション

// main.c
#include <gtk/gtk.h>

static void
print_hello (GtkWidget *widget,
             gpointer   data)
{
  g_print ("Hello World\n");
}

static void
activate (GtkApplication *app,
          gpointer        user_data)
{
  GtkWidget *window;
  GtkWidget *button;
  GtkWidget *box;

  // メインウィンドウを作成
  window = gtk_application_window_new (app);
  gtk_window_set_title (GTK_WINDOW (window), "GTK Hello World");
  gtk_window_set_default_size (GTK_WINDOW (window), 300, 200);

  // レイアウトコンテナを作成
  box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
  gtk_widget_set_halign (box, GTK_ALIGN_CENTER);
  gtk_widget_set_valign (box, GTK_ALIGN_CENTER);

  gtk_window_set_child (GTK_WINDOW (window), box);

  // ボタンを作成
  button = gtk_button_new_with_label ("Hello World");

  // シグナルの接続
  g_signal_connect (button, "clicked", G_CALLBACK (print_hello), NULL);
  g_signal_connect_swapped (button, "clicked", G_CALLBACK (gtk_window_destroy), window);

  gtk_box_append (GTK_BOX (box), button);

  // ウィンドウを表示
  gtk_window_present (GTK_WINDOW (window));
}

int
main (int    argc,
      char **argv)
{
  GtkApplication *app;
  int status;

  app = gtk_application_new ("org.example.HelloWorld", G_APPLICATION_DEFAULT_FLAGS);
  g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
  status = g_application_run (G_APPLICATION (app), argc, argv);
  g_object_unref (app);

  return status;
}

GtkBuilderを使用したUI設計

<!-- interface.ui -->
<?xml version="1.0" encoding="UTF-8"?>
<interface>
  <object id="window" class="GtkWindow">
    <property name="title">GTK Builder Example</property>
    <property name="default-width">400</property>
    <property name="default-height">300</property>
    <child>
      <object id="main_box" class="GtkBox">
        <property name="orientation">vertical</property>
        <property name="spacing">10</property>
        <property name="margin-top">20</property>
        <property name="margin-bottom">20</property>
        <property name="margin-start">20</property>
        <property name="margin-end">20</property>
        
        <child>
          <object id="header_label" class="GtkLabel">
            <property name="label">GTK Builder Demo</property>
            <property name="halign">center</property>
            <style>
              <class name="title-1"/>
            </style>
          </object>
        </child>
        
        <child>
          <object id="input_frame" class="GtkFrame">
            <property name="label">入力エリア</property>
            <child>
              <object id="input_grid" class="GtkGrid">
                <property name="margin-top">10</property>
                <property name="margin-bottom">10</property>
                <property name="margin-start">10</property>
                <property name="margin-end">10</property>
                <property name="row-spacing">10</property>
                <property name="column-spacing">10</property>
                
                <child>
                  <object id="name_label" class="GtkLabel">
                    <property name="label">名前:</property>
                    <property name="halign">end</property>
                    <layout>
                      <property name="column">0</property>
                      <property name="row">0</property>
                    </layout>
                  </object>
                </child>
                
                <child>
                  <object id="name_entry" class="GtkEntry">
                    <property name="placeholder-text">お名前を入力してください</property>
                    <property name="hexpand">true</property>
                    <layout>
                      <property name="column">1</property>
                      <property name="row">0</property>
                    </layout>
                  </object>
                </child>
                
                <child>
                  <object id="message_label" class="GtkLabel">
                    <property name="label">メッセージ:</property>
                    <property name="halign">end</property>
                    <property name="valign">start</property>
                    <layout>
                      <property name="column">0</property>
                      <property name="row">1</property>
                    </layout>
                  </object>
                </child>
                
                <child>
                  <object id="message_textview" class="GtkTextView">
                    <property name="height-request">100</property>
                    <property name="hexpand">true</property>
                    <property name="vexpand">true</property>
                    <layout>
                      <property name="column">1</property>
                      <property name="row">1</property>
                    </layout>
                  </object>
                </child>
                
              </object>
            </child>
          </object>
        </child>
        
        <child>
          <object id="button_box" class="GtkBox">
            <property name="orientation">horizontal</property>
            <property name="spacing">10</property>
            <property name="halign">center</property>
            
            <child>
              <object id="submit_button" class="GtkButton">
                <property name="label">送信</property>
                <style>
                  <class name="suggested-action"/>
                </style>
              </object>
            </child>
            
            <child>
              <object id="clear_button" class="GtkButton">
                <property name="label">クリア</property>
              </object>
            </child>
            
          </object>
        </child>
        
        <child>
          <object id="output_frame" class="GtkFrame">
            <property name="label">出力</property>
            <child>
              <object id="output_label" class="GtkLabel">
                <property name="label">ここに結果が表示されます</property>
                <property name="margin-top">10</property>
                <property name="margin-bottom">10</property>
                <property name="margin-start">10</property>
                <property name="margin-end">10</property>
                <property name="wrap">true</property>
                <property name="selectable">true</property>
              </object>
            </child>
          </object>
        </child>
        
      </object>
    </child>
  </object>
</interface>
// builder_app.c
#include <gtk/gtk.h>

typedef struct {
    GtkWidget *window;
    GtkWidget *name_entry;
    GtkWidget *message_textview;
    GtkWidget *output_label;
    GtkTextBuffer *text_buffer;
} AppWidgets;

static void
on_submit_clicked (GtkButton *button, AppWidgets *widgets)
{
    const char *name;
    char *message;
    char *output_text;
    GtkTextIter start, end;
    
    // 名前を取得
    name = gtk_editable_get_text (GTK_EDITABLE (widgets->name_entry));
    
    // メッセージを取得
    gtk_text_buffer_get_bounds (widgets->text_buffer, &start, &end);
    message = gtk_text_buffer_get_text (widgets->text_buffer, &start, &end, FALSE);
    
    // 出力テキストを作成
    if (strlen (name) > 0 && strlen (message) > 0) {
        output_text = g_strdup_printf ("名前: %s\nメッセージ: %s", name, message);
    } else {
        output_text = g_strdup ("名前とメッセージを入力してください。");
    }
    
    // 結果を表示
    gtk_label_set_text (GTK_LABEL (widgets->output_label), output_text);
    
    g_free (message);
    g_free (output_text);
}

static void
on_clear_clicked (GtkButton *button, AppWidgets *widgets)
{
    // フィールドをクリア
    gtk_editable_set_text (GTK_EDITABLE (widgets->name_entry), "");
    gtk_text_buffer_set_text (widgets->text_buffer, "", -1);
    gtk_label_set_text (GTK_LABEL (widgets->output_label), "ここに結果が表示されます");
}

static void
activate (GtkApplication *app, gpointer user_data)
{
    AppWidgets *widgets = g_new0 (AppWidgets, 1);
    GtkBuilder *builder;
    GtkWidget *submit_button, *clear_button;
    GError *error = NULL;

    // BuilderでUIを読み込み
    builder = gtk_builder_new ();
    if (!gtk_builder_add_from_file (builder, "interface.ui", &error)) {
        g_printerr ("UIファイルの読み込みエラー: %s\n", error->message);
        g_error_free (error);
        return;
    }

    // ウィジェットを取得
    widgets->window = GTK_WIDGET (gtk_builder_get_object (builder, "window"));
    widgets->name_entry = GTK_WIDGET (gtk_builder_get_object (builder, "name_entry"));
    widgets->message_textview = GTK_WIDGET (gtk_builder_get_object (builder, "message_textview"));
    widgets->output_label = GTK_WIDGET (gtk_builder_get_object (builder, "output_label"));
    submit_button = GTK_WIDGET (gtk_builder_get_object (builder, "submit_button"));
    clear_button = GTK_WIDGET (gtk_builder_get_object (builder, "clear_button"));

    // TextBufferを取得
    widgets->text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widgets->message_textview));

    // アプリケーションとウィンドウを関連付け
    gtk_window_set_application (GTK_WINDOW (widgets->window), app);

    // シグナルの接続
    g_signal_connect (submit_button, "clicked", G_CALLBACK (on_submit_clicked), widgets);
    g_signal_connect (clear_button, "clicked", G_CALLBACK (on_clear_clicked), widgets);

    // ウィンドウを表示
    gtk_window_present (GTK_WINDOW (widgets->window));

    // Builderのクリーンアップ
    g_object_unref (builder);
}

int
main (int argc, char *argv[])
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.example.BuilderApp", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

描画とCanvasアプリケーション

// drawing_app.c
#include <gtk/gtk.h>

/* 描画用のサーフェス */
static cairo_surface_t *surface = NULL;

static void
clear_surface (void)
{
    cairo_t *cr;

    cr = cairo_create (surface);
    cairo_set_source_rgb (cr, 1, 1, 1);  // 白で塗りつぶし
    cairo_paint (cr);
    cairo_destroy (cr);
}

/* 描画エリアのリサイズ */
static void
resize_cb (GtkWidget *widget,
           int        width,
           int        height,
           gpointer   data)
{
    if (surface) {
        cairo_surface_destroy (surface);
        surface = NULL;
    }

    if (gtk_native_get_surface (gtk_widget_get_native (widget))) {
        surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
                                              gtk_widget_get_width (widget),
                                              gtk_widget_get_height (widget));
        clear_surface ();
    }
}

/* 描画コールバック */
static void
draw_cb (GtkDrawingArea *drawing_area,
         cairo_t        *cr,
         int             width,
         int             height,
         gpointer        data)
{
    cairo_set_source_surface (cr, surface, 0, 0);
    cairo_paint (cr);
}

/* ブラシで描画 */
static void
draw_brush (GtkWidget *widget, double x, double y)
{
    cairo_t *cr;

    if (surface == NULL)
        return;

    cr = cairo_create (surface);

    cairo_rectangle (cr, x - 3, y - 3, 6, 6);
    cairo_fill (cr);

    cairo_destroy (cr);

    /* 描画エリアを更新 */
    gtk_widget_queue_draw (widget);
}

/* ドラッグ開始 */
static void
drag_begin (GtkGestureDrag *gesture,
            double          x,
            double          y,
            GtkWidget      *area)
{
    draw_brush (area, x, y);
}

/* ドラッグ更新 */
static void
drag_update (GtkGestureDrag *gesture,
             double          x,
             double          y,
             GtkWidget      *area)
{
    double start_x, start_y;
    gtk_gesture_drag_get_start_point (gesture, &start_x, &start_y);
    draw_brush (area, start_x + x, start_y + y);
}

/* 右クリックでクリア */
static void
pressed (GtkGestureClick *gesture,
         int              n_press,
         double           x,
         double           y,
         GtkWidget       *area)
{
    clear_surface ();
    gtk_widget_queue_draw (area);
}

/* ウィンドウクローズ時のクリーンアップ */
static void
close_window (void)
{
    if (surface)
        cairo_surface_destroy (surface);
}

static void
activate (GtkApplication *app, gpointer user_data)
{
    GtkWidget *window;
    GtkWidget *frame;
    GtkWidget *drawing_area;
    GtkGesture *drag;
    GtkGesture *press;

    // メインウィンドウを作成
    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "描画アプリケーション");
    gtk_window_set_default_size (GTK_WINDOW (window), 600, 400);

    g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);

    // フレームを作成
    frame = gtk_frame_new (NULL);
    gtk_window_set_child (GTK_WINDOW (window), frame);

    // 描画エリアを作成
    drawing_area = gtk_drawing_area_new ();
    gtk_widget_set_size_request (drawing_area, 100, 100);

    gtk_frame_set_child (GTK_FRAME (frame), drawing_area);

    // 描画コールバックを設定
    gtk_drawing_area_set_draw_func (GTK_DRAWING_AREA (drawing_area), draw_cb, NULL, NULL);

    // リサイズシグナルを接続
    g_signal_connect_after (drawing_area, "resize", G_CALLBACK (resize_cb), NULL);

    // ドラッグジェスチャーを設定(左ボタン)
    drag = gtk_gesture_drag_new ();
    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (drag), GDK_BUTTON_PRIMARY);
    gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (drag));
    g_signal_connect (drag, "drag-begin", G_CALLBACK (drag_begin), drawing_area);
    g_signal_connect (drag, "drag-update", G_CALLBACK (drag_update), drawing_area);

    // クリックジェスチャーを設定(右ボタン)
    press = gtk_gesture_click_new ();
    gtk_gesture_single_set_button (GTK_GESTURE_SINGLE (press), GDK_BUTTON_SECONDARY);
    gtk_widget_add_controller (drawing_area, GTK_EVENT_CONTROLLER (press));
    g_signal_connect (press, "pressed", G_CALLBACK (pressed), drawing_area);

    // ウィンドウを表示
    gtk_window_present (GTK_WINDOW (window));
}

int
main (int argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.example.DrawingApp", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

リストビューとモデル

// listview_app.c
#include <gtk/gtk.h>

/* データアイテムの構造 */
typedef struct {
    GObject parent_instance;
    char *name;
    char *description;
    int priority;
} TaskItem;

typedef struct {
    GObjectClass parent_class;
} TaskItemClass;

G_DEFINE_TYPE (TaskItem, task_item, G_TYPE_OBJECT)

static void
task_item_finalize (GObject *object)
{
    TaskItem *item = (TaskItem *)object;
    g_free (item->name);
    g_free (item->description);
    G_OBJECT_CLASS (task_item_parent_class)->finalize (object);
}

static void
task_item_class_init (TaskItemClass *class)
{
    G_OBJECT_CLASS (class)->finalize = task_item_finalize;
}

static void
task_item_init (TaskItem *item)
{
    // 初期化処理
}

static TaskItem *
task_item_new (const char *name, const char *description, int priority)
{
    TaskItem *item = g_object_new (task_item_get_type (), NULL);
    item->name = g_strdup (name);
    item->description = g_strdup (description);
    item->priority = priority;
    return item;
}

/* リストアイテムの設定 */
static void
setup_listitem (GtkSignalListItemFactory *factory,
                GtkListItem              *list_item)
{
    GtkWidget *box, *name_label, *desc_label, *priority_label;

    box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 4);
    gtk_widget_set_margin_top (box, 8);
    gtk_widget_set_margin_bottom (box, 8);
    gtk_widget_set_margin_start (box, 12);
    gtk_widget_set_margin_end (box, 12);

    name_label = gtk_label_new (NULL);
    gtk_label_set_xalign (GTK_LABEL (name_label), 0.0);
    gtk_widget_add_css_class (name_label, "heading");
    
    desc_label = gtk_label_new (NULL);
    gtk_label_set_xalign (GTK_LABEL (desc_label), 0.0);
    gtk_widget_add_css_class (desc_label, "dim-label");
    
    priority_label = gtk_label_new (NULL);
    gtk_label_set_xalign (GTK_LABEL (priority_label), 1.0);
    gtk_widget_add_css_class (priority_label, "caption");

    gtk_box_append (GTK_BOX (box), name_label);
    gtk_box_append (GTK_BOX (box), desc_label);
    gtk_box_append (GTK_BOX (box), priority_label);

    gtk_list_item_set_child (list_item, box);

    g_object_set_data (G_OBJECT (list_item), "name_label", name_label);
    g_object_set_data (G_OBJECT (list_item), "desc_label", desc_label);
    g_object_set_data (G_OBJECT (list_item), "priority_label", priority_label);
}

/* リストアイテムのデータバインディング */
static void
bind_listitem (GtkSignalListItemFactory *factory,
               GtkListItem              *list_item)
{
    GtkWidget *name_label, *desc_label, *priority_label;
    TaskItem *item;
    char *priority_text;

    item = gtk_list_item_get_item (list_item);

    name_label = g_object_get_data (G_OBJECT (list_item), "name_label");
    desc_label = g_object_get_data (G_OBJECT (list_item), "desc_label");
    priority_label = g_object_get_data (G_OBJECT (list_item), "priority_label");

    gtk_label_set_text (GTK_LABEL (name_label), item->name);
    gtk_label_set_text (GTK_LABEL (desc_label), item->description);
    
    priority_text = g_strdup_printf ("優先度: %d", item->priority);
    gtk_label_set_text (GTK_LABEL (priority_label), priority_text);
    g_free (priority_text);
}

static void
activate (GtkApplication *app, gpointer user_data)
{
    GtkWidget *window;
    GtkWidget *scrolled_window;
    GtkWidget *list_view;
    GListStore *model;
    GtkListItemFactory *factory;
    GtkSingleSelection *selection_model;

    // メインウィンドウを作成
    window = gtk_application_window_new (app);
    gtk_window_set_title (GTK_WINDOW (window), "タスクリスト");
    gtk_window_set_default_size (GTK_WINDOW (window), 400, 600);

    // スクロールウィンドウを作成
    scrolled_window = gtk_scrolled_window_new ();
    gtk_window_set_child (GTK_WINDOW (window), scrolled_window);

    // リストモデルを作成
    model = g_list_store_new (task_item_get_type ());

    // サンプルデータを追加
    g_list_store_append (model, task_item_new ("ドキュメント作成", "プロジェクトの技術仕様書を作成する", 1));
    g_list_store_append (model, task_item_new ("コードレビュー", "チームメンバーのコードをレビューする", 2));
    g_list_store_append (model, task_item_new ("バグ修正", "報告されたバグを修正する", 1));
    g_list_store_append (model, task_item_new ("新機能開発", "ユーザー認証機能を実装する", 3));
    g_list_store_append (model, task_item_new ("テスト作成", "単体テストとE2Eテストを作成する", 2));

    // セレクションモデルを作成
    selection_model = gtk_single_selection_new (G_LIST_MODEL (model));

    // リストアイテムファクトリーを作成
    factory = gtk_signal_list_item_factory_new ();
    g_signal_connect (factory, "setup", G_CALLBACK (setup_listitem), NULL);
    g_signal_connect (factory, "bind", G_CALLBACK (bind_listitem), NULL);

    // リストビューを作成
    list_view = gtk_list_view_new (GTK_SELECTION_MODEL (selection_model), factory);
    gtk_scrolled_window_set_child (GTK_SCROLLED_WINDOW (scrolled_window), list_view);

    // ウィンドウを表示
    gtk_window_present (GTK_WINDOW (window));
}

int
main (int argc, char **argv)
{
    GtkApplication *app;
    int status;

    app = gtk_application_new ("org.example.TaskListApp", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
    status = g_application_run (G_APPLICATION (app), argc, argv);
    g_object_unref (app);

    return status;
}

CSSスタイリング

/* style.css */
@import url("resource:///org/gtk/libgtk/theme/Adwaita/gtk.css");

.main-window {
  background-color: #f6f5f4;
  color: #2e3436;
}

.title-1 {
  font-size: 2em;
  font-weight: bold;
  color: #1c71d8;
  margin-bottom: 1em;
}

.card {
  background-color: white;
  border-radius: 12px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
  margin: 12px;
  padding: 16px;
}

.header-bar {
  background: linear-gradient(to bottom, #3584e4, #1c71d8);
  color: white;
}

.sidebar {
  background-color: #fafafa;
  border-right: 1px solid #d3d3d3;
  min-width: 250px;
}

.content-area {
  padding: 20px;
  background-color: white;
}

.suggested-action {
  background: linear-gradient(to bottom, #57e389, #33d17a);
  color: white;
  border: none;
  border-radius: 6px;
  padding: 8px 16px;
  font-weight: bold;
}

.suggested-action:hover {
  background: linear-gradient(to bottom, #33d17a, #26a269);
}

.destructive-action {
  background: linear-gradient(to bottom, #f66151, #e01b24);
  color: white;
  border: none;
  border-radius: 6px;
  padding: 8px 16px;
  font-weight: bold;
}

.destructive-action:hover {
  background: linear-gradient(to bottom, #e01b24, #a51d2d);
}

.entry {
  border: 1px solid #d3d3d3;
  border-radius: 6px;
  padding: 8px 12px;
  background-color: white;
}

.entry:focus {
  border-color: #3584e4;
  box-shadow: 0 0 0 2px rgba(53, 132, 228, 0.3);
}

.frame {
  border: 1px solid #d3d3d3;
  border-radius: 6px;
  background-color: white;
}

.frame > label {
  color: #5e5c64;
  font-weight: bold;
  padding: 0 6px;
  background-color: white;
}

listview row {
  border-bottom: 1px solid #f0f0f0;
  padding: 8px;
}

listview row:hover {
  background-color: rgba(53, 132, 228, 0.1);
}

listview row:selected {
  background-color: #3584e4;
  color: white;
}

.dim-label {
  opacity: 0.65;
}

.caption {
  font-size: 0.8em;
  opacity: 0.75;
}

.status-bar {
  background-color: #f0f0f0;
  border-top: 1px solid #d3d3d3;
  padding: 4px 8px;
}

.toolbar {
  background: linear-gradient(to bottom, #fafafa, #ededed);
  border-bottom: 1px solid #d3d3d3;
  padding: 6px;
}

.circular {
  border-radius: 50%;
}

.warning {
  color: #e5a50a;
}

.error {
  color: #e01b24;
}

.success {
  color: #26a269;
}

Mesonビルド設定

# meson.build
project('gtk-demo-app', 'c',
  version : '1.0.0',
  default_options : ['warning_level=3',
                     'c_std=c11'])

# 依存関係
gtk_dep = dependency('gtk4')
glib_dep = dependency('glib-2.0')

# リソースファイルの処理
gnome = import('gnome')

resources = gnome.compile_resources('app-resources',
  'resources.gresource.xml',
  source_dir: 'resources')

# 実行ファイル
executable('gtk-demo-app',
  ['main.c', 'window.c'] + resources,
  dependencies : [gtk_dep, glib_dep],
  install : true)

# デスクトップファイル
install_data('org.example.GtkDemoApp.desktop',
  install_dir : get_option('datadir') / 'applications')

# アイコン
install_data('icons/org.example.GtkDemoApp.svg',
  install_dir : get_option('datadir') / 'icons/hicolor/scalable/apps')
<!-- resources.gresource.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
  <gresource prefix="/org/example/GtkDemoApp">
    <file preprocess="xml-stripblanks">ui/window.ui</file>
    <file preprocess="xml-stripblanks">ui/preferences.ui</file>
    <file>css/style.css</file>
    <file>icons/preferences-symbolic.svg</file>
    <file>icons/help-symbolic.svg</file>
  </gresource>
</gresources>

ビルドとインストール

# Mesonプロジェクトの設定
meson setup builddir

# ビルド
meson compile -C builddir

# テスト実行
meson test -C builddir

# インストール
meson install -C builddir

# 開発用実行
./builddir/gtk-demo-app

# クリーンビルド
rm -rf builddir
meson setup builddir
meson compile -C builddir

pkg-configを使用したコンパイル

# 単一ファイルのコンパイル
gcc -o hello `pkg-config --cflags --libs gtk4` hello.c

# 複数ファイルのコンパイル
gcc -o myapp `pkg-config --cflags --libs gtk4` main.c window.c

# デバッグビルド
gcc -g -O0 -o myapp `pkg-config --cflags --libs gtk4` *.c

# 最適化ビルド
gcc -O2 -o myapp `pkg-config --cflags --libs gtk4` *.c

プラットフォーム別の特別な考慮事項

Linux

  • パッケージマネージャーでの簡単インストール
  • 豊富なテーマとアイコンセット
  • Waylandプロトコル完全サポート
  • システム統合(デスクトップファイル、DBus)

Windows

  • MSYS2やVcpkgでの開発環境構築
  • Windows APIとの相互運用性
  • HiDPIサポート
  • MSIインストーラー作成

macOS

  • Homebrewでのインストール
  • macOSルック&フィール調整
  • App Bundleパッケージング
  • macOS特有のUI/UXガイドライン対応