gtk-ui-ux-engineer

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

GTK UI/UX Engineer

GTK UI/UX 工程师

A GTK (GTK4/GTK3) UI/UX specialist who crafts beautiful, native-feeling desktop applications following GNOME Human Interface Guidelines and modern GTK4 best practices.
一位遵循GNOME人机交互指南(GNOME Human Interface Guidelines)与现代GTK4最佳实践,打造美观、原生质感桌面应用的GTK(GTK4/GTK3)UI/UX专家。

Design Philosophy

设计理念

Purpose

目标

  • Create visually stunning GTK applications that feel native to the Linux desktop
  • Follow GNOME Human Interface Guidelines (HIG) while pushing aesthetic boundaries
  • Balance platform integration with distinctive visual identity
  • Prioritize accessibility, responsiveness, and theme-awareness
  • Ship production-grade code with proper memory management and modern GTK4 patterns
  • 创建在Linux桌面环境中具有原生体验的精美GTK应用
  • 遵循GNOME人机交互指南(HIG)的同时突破美学边界
  • 在平台集成与独特视觉标识之间取得平衡
  • 优先考虑无障碍性、响应式与主题适配性
  • 交付符合生产级标准的代码,包含完善的内存管理与现代GTK4模式

Tone

风格

  • Native-first: Embrace platform conventions (header bars, Adwaita, libadwaita)
  • Bold aesthetics: Don't settle for "default" GTK styling - make visual statements
  • Accessibility-obsessed: Every UI decision considers screen readers, keyboard navigation, high contrast
  • Performance-conscious: Efficient list views, minimal redraws, proper GObject lifecycle
  • 原生优先:遵循平台规范(标题栏、Adwaita、libadwaita)
  • 大胆美学:不满足于GTK默认样式,打造鲜明视觉效果
  • 无障碍至上:每一项UI决策都兼顾屏幕阅读器、键盘导航与高对比度需求
  • 性能优先:高效列表视图、最少重绘次数、规范的GObject生命周期

Constraints

约束

  • MUST follow GNOME HIG principles for platform integration
  • MUST use modern GTK4 APIs (GtkApplication, event controllers, GtkListView)
  • MUST support light/dark modes with AdwStyleManager
  • MUST use CSS variables for theme-aware styling
  • MUST NOT use deprecated GTK3 APIs when GTK4 alternatives exist
  • MUST NOT block the main loop with synchronous operations
  • MUST NOT mix GTK3 and GTK4 APIs
  • 必须遵循GNOME HIG原则以实现平台集成
  • 必须使用现代GTK4 API(GtkApplication、事件控制器、GtkListView)
  • 必须通过AdwStyleManager支持明暗模式
  • 必须使用CSS变量实现主题适配样式
  • 当GTK4存在替代方案时,禁止使用已废弃的GTK3 API
  • 禁止通过同步操作阻塞主循环
  • 禁止混合使用GTK3与GTK4 API

Differentiation

差异化优势

  • Unlike generic GTK tutorials that show basic widget usage, this skill emphasizes:
    • Visual Impact: Custom CSS, unique accent colors, deliberate animations
    • Modern Patterns: GtkListView with factories, GActions, property bindings
    • Platform Integration: Header bars, libadwaita widgets, GSettings
    • Real-World Examples: Patterns from GNOME Text Editor, Nautilus, GIMP

  • 与仅展示基础组件用法的通用GTK教程不同,本技能强调:
    • 视觉冲击力:自定义CSS、独特强调色、精心设计的动画
    • 现代模式:结合工厂模式的GtkListView、GActions、属性绑定
    • 平台集成:标题栏、libadwaita组件、GSettings
    • 真实场景示例:来自GNOME Text Editor、Nautilus、GIMP的设计模式

GTK4 CSS Syntax Requirements

GTK4 CSS语法要求

CRITICAL: GTK4 uses different CSS syntax than GTK3. Always use:
  • var(--variable)
    for CSS variables (GTK4)
  • @variable
    for GTK3 variables (deprecated, doesn't work)
  • color-mix()
    for color blending (GTK4)
  • filter: brightness()
    (not supported in GTK4)
  • ✅ Media queries:
    @media (prefers-color-scheme: dark)
  • ✅ Libadwaita variables:
    var(--window-bg-color)
GTK3 Syntax That Does NOT Work in GTK4:
css
/* ❌ GTK3 syntax - breaks in GTK4 */
@define-color my_color red;
@define-color window_bg var(--window-bg);
color: @my_color;
GTK4 Correct Syntax:
css
/* ✅ GTK4 syntax - correct */
:root {
  --my-color: red;
  --window-bg: var(--window-bg-color);
}

.card {
  background-color: var(--my-color);
  color: var(--window-bg);
}
For detailed CSS styling guidance, see
references/libadwaita-styling.md

重要提示:GTK4使用与GTK3不同的CSS语法,请始终使用:
  • var(--variable)
    表示CSS变量(GTK4标准)
  • @variable
    为GTK3变量语法(已废弃,在GTK4中无效)
  • color-mix()
    用于颜色混合(GTK4支持)
  • filter: brightness()
    (GTK4不支持)
  • ✅ 媒体查询:
    @media (prefers-color-scheme: dark)
  • ✅ Libadwaita变量:
    var(--window-bg-color)
在GTK4中无效的GTK3语法:
css
/* ❌ GTK3语法 - 在GTK4中会失效 */
@define-color my_color red;
@define-color window_bg var(--window-bg);
color: @my_color;
GTK4正确语法:
css
/* ✅ GTK4语法 - 正确写法 */
:root {
  --my-color: red;
  --window-bg: var(--window-bg-color);
}

.card {
  background-color: var(--my-color);
  color: var(--window-bg);
}
如需详细的CSS样式指导,请查看
references/libadwaita-styling.md

GTK4 UI/UX Aesthetics

GTK4 UI/UX美学设计

Typography & Icons

排版与图标

Font Stack

字体栈

css
/* Use system fonts for platform consistency */
window {
  font-family: "Cantarell", system-ui, sans-serif;
  font-weight: 400;
}

/* Headlines get emphasis */
.title {
  font-family: "Cantarell", system-ui, sans-serif;
  font-weight: 700;
  font-size: 24pt;
}

/* Monospace for code */
.code {
  font-family: "JetBrains Mono", "Monospace", monospace;
}
css
/* 使用系统字体保证平台一致性 */
window {
  font-family: "Cantarell", system-ui, sans-serif;
  font-weight: 400;
}

/* 标题文字强调显示 */
.title {
  font-family: "Cantarell", system-ui, sans-serif;
  font-weight: 700;
  font-size: 24pt;
}

/* 代码使用等宽字体 */
.code {
  font-family: "JetBrains Mono", "Monospace", monospace;
}

Icon Usage

图标使用

  • Use symbolic icons from GNOME icon theme (e.g.,
    document-symbolic
    ,
    edit-find-symbolic
    )
  • Scale icons with icon size CSS properties
  • Color icons using CSS
    color: var(--accent-bg-color);
Example:
c
// Add symbolic icon to button
GtkWidget *button = gtk_button_new_from_icon_name("document-open-symbolic");
gtk_button_set_icon_name(GTK_BUTTON(button), "document-save-symbolic");
  • 使用GNOME图标主题中的符号化图标(例如:
    document-symbolic
    edit-find-symbolic
  • 通过CSS属性缩放图标
  • 使用CSS
    color: var(--accent-bg-color);
    设置图标颜色
示例:
c
// 为按钮添加符号化图标
GtkWidget *button = gtk_button_new_from_icon_name("document-open-symbolic");
gtk_button_set_icon_name(GTK_BUTTON(button), "document-save-symbolic");

Color & Theming

颜色与主题

Theme-Aware Color System

主题适配颜色系统

css
/* Use CSS variables for theme integration */
:root {
  /* Override accent color for brand identity */
  --accent-bg-color: var(--accent-blue);
  --accent-fg-color: white;
}

/* Custom accent color - e.g., purple brand */
:root {
  --accent-bg-color: var(--accent-purple);
  --accent-color: oklab(from var(--accent-bg-color) var(--standalone-color-oklab));
}

/* Dark mode overrides */
@media (prefers-color-scheme: dark) {
  .card {
    background-color: rgba(255, 255, 255, 0.05);
  }
}

/* High contrast mode */
@media (prefers-contrast: more) {
  .card {
    border: 2px solid currentColor;
  }
}
css
/* 使用CSS变量实现主题集成 */
:root {
  /* 覆盖强调色以塑造品牌标识 */
  --accent-bg-color: var(--accent-blue);
  --accent-fg-color: white;
}

/* 自定义强调色 - 例如紫色品牌风格 */
:root {
  --accent-bg-color: var(--accent-purple);
  --accent-color: oklab(from var(--accent-bg-color) var(--standalone-color-oklab));
}

/* 深色模式覆盖样式 */
@media (prefers-color-scheme: dark) {
  .card {
    background-color: rgba(255, 255, 255, 0.05);
  }
}

/* 高对比度模式 */
@media (prefers-contrast: more) {
  .card {
    border: 2px solid currentColor;
  }
}

Visual Hierarchy

视觉层次

css
/* Cards with subtle shadows */
.card {
  background-color: var(--card-bg-color);
  border-radius: 12px;
  padding: 16px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

/* Primary buttons use accent color */
.primary-button {
  background-color: var(--accent-bg-color);
  color: var(--accent-fg-color);
  padding: 8px 16px;
  border-radius: 8px;
}

.primary-button:hover {
  /* GTK4: Use color-mix() instead of filter: brightness() */
  background-color: color-mix(in srgb, var(--accent-bg-color) 90%, white);
}
css
/* 带有柔和阴影的卡片 */
.card {
  background-color: var(--card-bg-color);
  border-radius: 12px;
  padding: 16px;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.08);
}

/* 主按钮使用强调色 */
.primary-button {
  background-color: var(--accent-bg-color);
  color: var(--accent-fg-color);
  padding: 8px 16px;
  border-radius: 8px;
}

.primary-button:hover {
  /* GTK4:使用color-mix()替代filter: brightness() */
  background-color: color-mix(in srgb, var(--accent-bg-color) 90%, white);
}

Spacing & Layout

间距与布局

Spacing Scale

间距尺度

css
/* 4px base unit */
.space-xs  { padding: 4px; }
.space-sm  { padding: 8px; }
.space-md  { padding: 12px; }
.space-lg  { padding: 16px; }
.space-xl  { padding: 24px; }
.space-2xl { padding: 32px; }
css
/* 以4px为基础单位 */
.space-xs  { padding: 4px; }
.space-sm  { padding: 8px; }
.space-md  { padding: 12px; }
.space-lg  { padding: 16px; }
.space-xl  { padding: 24px; }
.space-2xl { padding: 32px; }

Border Radius

圆角设置

css
/* Match Adwaita conventions */
button {
  border-radius: 8px;
}

window {
  border-radius: 12px;
}

.card {
  border-radius: 12px;
}
css
/* 匹配Adwaita规范 */
button {
  border-radius: 8px;
}

window {
  border-radius: 12px;
}

.card {
  border-radius: 12px;
}

Motion & Animation

动效与动画

Delicate Transitions

细腻过渡

css
/* Smooth property transitions */
button {
  transition: background-color 200ms ease,
              transform 100ms ease,
              box-shadow 200ms ease;
}

button:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
}

button:active {
  transform: translateY(0);
}
css
/* 平滑的属性过渡 */
button {
  transition: background-color 200ms ease,
              transform 100ms ease,
              box-shadow 200ms ease;
}

button:hover {
  transform: translateY(-1px);
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.12);
}

button:active {
  transform: translateY(0);
}

Purposeful Motion

有意义的动效

css
/* Fade in for new content */
fade-in {
  animation: fadeIn 300ms ease-out;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Avoid excessive animations - use sparingly for emphasis */
css
/* 新内容淡入效果 */
fade-in {
  animation: fadeIn 300ms ease-out;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* 避免过度动画 - 仅在需要强调时使用 */

Responsive Design

响应式设计

Adaptive Containers

自适应容器

c
/* Use AdwLeaflet for mobile-first layouts */
GtkWidget *leaflet = adw_leaflet_new();
adw_leaflet_set_collapsed(GTK_LEAFLET(leaflet), TRUE);

/* Content adapts based on fold state */
g_signal_connect(leaflet, "notify::folded",
                G_CALLBACK(on_fold_changed), NULL);
c
/* 使用AdwLeaflet实现移动端优先布局 */
GtkWidget *leaflet = adw_leaflet_new();
adw_leaflet_set_collapsed(GTK_LEAFLET(leaflet), TRUE);

/* 根据折叠状态适配内容 */
g_signal_connect(leaflet, "notify::folded",
                G_CALLBACK(on_fold_changed), NULL);

Breakpoint-Based Styling

断点式样式

css
/* Compact layouts for narrow windows */
window {
  min-width: 360px;
  min-height: 240px;
}

@media (max-width: 600px) {
  .sidebar {
    display: none;
  }
}

/* Use AdwBreakpoint for more complex responsive behavior */
css
/* 窄窗口下的紧凑布局 */
window {
  min-width: 360px;
  min-height: 240px;
}

@media (max-width: 600px) {
  .sidebar {
    display: none;
  }
}

/* 更复杂的响应式行为使用AdwBreakpoint */

Accessibility

无障碍性

Keyboard Navigation

键盘导航

c
// Ensure all interactive elements are keyboard focusable
gtk_widget_set_can_focus(widget, TRUE);

// Set focus order
gtk_widget_set_focus_on_click(button, TRUE);

// Mnemonic shortcuts
gtk_label_set_mnemonic_widget(label, entry);
c
// 确保所有交互元素可通过键盘聚焦
gtk_widget_set_can_focus(widget, TRUE);

// 设置聚焦顺序
gtk_widget_set_focus_on_click(button, TRUE);

// 助记快捷键
gtk_label_set_mnemonic_widget(label, entry);

Accessible Labels

无障碍标签

c
// Label widgets properly
gtk_accessible_update_property(GTK_ACCESSIBLE(widget),
    GTK_ACCESSIBLE_PROPERTY_LABEL, "Save changes",
    -1
);

// Add descriptions for complex widgets
gtk_accessible_update_property(GTK_ACCESSIBLE(widget),
    GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
    "Saves the current document to disk",
    -1
);
c
// 为组件设置正确标签
gtk_accessible_update_property(GTK_ACCESSIBLE(widget),
    GTK_ACCESSIBLE_PROPERTY_LABEL, "保存更改",
    -1
);

// 为复杂组件添加描述
gtk_accessible_update_property(GTK_ACCESSIBLE(widget),
    GTK_ACCESSIBLE_PROPERTY_DESCRIPTION,
    "将当前文档保存到磁盘",
    -1
);

High Contrast Support

高对比度支持

css
@media (prefers-contrast: more) {
  * {
    border: 1px solid currentColor;
  }

  button {
    background-color: transparent;
    border: 2px solid currentColor;
  }
}

css
@media (prefers-contrast: more) {
  * {
    border: 1px solid currentColor;
  }

  button {
    background-color: transparent;
    border: 2px solid currentColor;
  }
}

GTK4 Architecture & Patterns

GTK4架构与模式

Application Structure

应用结构

Modern GTK4 Application Pattern

现代GTK4应用模式

c
// Subclass GtkApplication
struct _MyApp {
    GtkApplication parent;
    GSettings *settings;
};

G_DEFINE_TYPE(MyApp, my_app, GTK_TYPE_APPLICATION);

static void my_app_activate(GApplication *app) {
    GtkWindow *window = gtk_application_window_new(GTK_APPLICATION(app));

    // Create window content
    MyWindow *my_window = my_window_new(GTK_APPLICATION(app));
    gtk_window_present(GTK_WINDOW(my_window));
}
c
// 继承GtkApplication
struct _MyApp {
    GtkApplication parent;
    GSettings *settings;
};

G_DEFINE_TYPE(MyApp, my_app, GTK_TYPE_APPLICATION);

static void my_app_activate(GApplication *app) {
    GtkWindow *window = gtk_application_window_new(GTK_APPLICATION(app));

    // 创建窗口内容
    MyWindow *my_window = my_window_new(GTK_APPLICATION(app));
    gtk_window_present(GTK_WINDOW(my_window));
}

Window Template Pattern

窗口模板模式

c
// Class init - load UI from resource
static void my_window_class_init(MyWindowClass *klass) {
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    gtk_widget_class_set_template_from_resource(widget_class,
        "/org/example/app/ui/window.ui");

    // Bind widgets
    gtk_widget_class_bind_template_child(widget_class, MyWindow, header_bar);
    gtk_widget_class_bind_template_child(widget_class, MyWindow, content_box);
}

// Instance init - initialize template
static void my_window_init(MyWindow *self) {
    gtk_widget_init_template(GTK_WIDGET(self));
}
c
// 类初始化 - 从资源加载UI
static void my_window_class_init(MyWindowClass *klass) {
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    gtk_widget_class_set_template_from_resource(widget_class,
        "/org/example/app/ui/window.ui");

    // 绑定组件
    gtk_widget_class_bind_template_child(widget_class, MyWindow, header_bar);
    gtk_widget_class_bind_template_child(widget_class, MyWindow, content_box);
}

// 实例初始化 - 初始化模板
static void my_window_init(MyWindow *self) {
    gtk_widget_init_template(GTK_WIDGET(self));
}

Widget Composition

组件组合

Composite Widgets

复合组件

c
// Create reusable composite widgets
struct _MyCompositeWidget {
    GtkBox parent;
    GtkLabel *title_label;
    GtkButton *action_button;
};

G_DEFINE_TYPE(MyCompositeWidget, my_composite_widget, GTK_TYPE_BOX);

static void my_composite_widget_init(MyCompositeWidget *self) {
    gtk_orientable_set_orientation(GTK_ORIENTABLE(self),
                                    GTK_ORIENTATION_VERTICAL);
    gtk_box_set_spacing(GTK_BOX(self), 6);

    // Create children
    self->title_label = gtk_label_new(NULL);
    gtk_widget_add_css_class(GTK_WIDGET(self->title_label), "title");
    gtk_box_append(GTK_BOX(self), GTK_WIDGET(self->title_label));

    self->action_button = gtk_button_new_with_label("Action");
    gtk_box_append(GTK_BOX(self), GTK_WIDGET(self->action_button));
}
c
// 创建可复用的复合组件
struct _MyCompositeWidget {
    GtkBox parent;
    GtkLabel *title_label;
    GtkButton *action_button;
};

G_DEFINE_TYPE(MyCompositeWidget, my_composite_widget, GTK_TYPE_BOX);

static void my_composite_widget_init(MyCompositeWidget *self) {
    gtk_orientable_set_orientation(GTK_ORIENTABLE(self),
                                    GTK_ORIENTATION_VERTICAL);
    gtk_box_set_spacing(GTK_BOX(self), 6);

    // 创建子组件
    self->title_label = gtk_label_new(NULL);
    gtk_widget_add_css_class(GTK_WIDGET(self->title_label), "title");
    gtk_box_append(GTK_BOX(self), GTK_WIDGET(self->title_label));

    self->action_button = gtk_button_new_with_label("操作");
    gtk_box_append(GTK_BOX(self), GTK_WIDGET(self->action_button));
}

List Views with Factories

结合工厂模式的列表视图

c
// Modern GtkListView pattern
static void setup_listitem_cb(GtkListItem *list_item, gpointer user_data) {
    GtkWidget *label = gtk_label_new(NULL);
    gtk_list_item_set_child(list_item, label);
}

static void bind_listitem_cb(GtkListItem *list_item, gpointer user_data) {
    GObject *item = gtk_list_item_get_item(list_item);
    GtkWidget *label = gtk_list_item_get_child(list_item);
    const char *text = my_item_get_text(MY_ITEM(item));
    gtk_label_set_text(GTK_LABEL(label), text);
}

// Create list view
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
g_signal_connect(factory, "setup", G_CALLBACK(setup_listitem_cb), NULL);
g_signal_connect(factory, "bind", G_CALLBACK(bind_listitem_cb), NULL);

GtkSelectionModel *model = gtk_single_selection_new(G_LIST_MODEL(create_model()));
GtkWidget *listview = gtk_list_view_new(model, factory);
c
// 现代GtkListView模式
static void setup_listitem_cb(GtkListItem *list_item, gpointer user_data) {
    GtkWidget *label = gtk_label_new(NULL);
    gtk_list_item_set_child(list_item, label);
}

static void bind_listitem_cb(GtkListItem *list_item, gpointer user_data) {
    GObject *item = gtk_list_item_get_item(list_item);
    GtkWidget *label = gtk_list_item_get_child(list_item);
    const char *text = my_item_get_text(MY_ITEM(item));
    gtk_label_set_text(GTK_LABEL(label), text);
}

// 创建列表视图
GtkListItemFactory *factory = gtk_signal_list_item_factory_new();
g_signal_connect(factory, "setup", G_CALLBACK(setup_listitem_cb), NULL);
g_signal_connect(factory, "bind", G_CALLBACK(bind_listitem_cb), NULL);

GtkSelectionModel *model = gtk_single_selection_new(G_LIST_MODEL(create_model()));
GtkWidget *listview = gtk_list_view_new(model, factory);

Actions & Menus

操作与菜单

GAction Architecture

GAction架构

c
// Define action entries
static GActionEntry app_entries[] = {
    { "quit", on_quit, NULL, NULL },
    { "preferences", on_preferences, NULL, NULL },
    { "about", on_about, NULL, NULL }
};

// Add actions in startup
static void on_startup(GApplication *app) {
    g_action_map_add_action_entries(G_ACTION_MAP(app),
                                     app_entries,
                                     G_N_ELEMENTS(app_entries),
                                     app);

    // Set accelerators
    const char *quit_accels[] = { "<Control>q", NULL };
    gtk_application_set_accels_for_action(GTK_APPLICATION(app),
                                           "app.quit",
                                           quit_accels);
}
c
// 定义操作条目
static GActionEntry app_entries[] = {
    { "quit", on_quit, NULL, NULL },
    { "preferences", on_preferences, NULL, NULL },
    { "about", on_about, NULL, NULL }
};

// 在启动时添加操作
static void on_startup(GApplication *app) {
    g_action_map_add_action_entries(G_ACTION_MAP(app),
                                     app_entries,
                                     G_N_ELEMENTS(app_entries),
                                     app);

    // 设置快捷键
    const char *quit_accels[] = { "<Control>q", NULL };
    gtk_application_set_accels_for_action(GTK_APPLICATION(app),
                                           "app.quit",
                                           quit_accels);
}

Menu Integration

菜单集成

xml
<!-- Define menu in .ui file -->
<menu id="app_menu">
  <section>
    <item>
      <attribute name="label">_Preferences</attribute>
      <attribute name="action">app.preferences</attribute>
      <attribute name="verb-icon">preferences-system-symbolic</attribute>
    </item>
  </section>
  <section>
    <item>
      <attribute name="label">_About</attribute>
      <attribute name="action">app.about</attribute>
    </item>
  </section>
</menu>
xml
<!-- 在.ui文件中定义菜单 -->
<menu id="app_menu">
  <section>
    <item>
      <attribute name="label">_偏好设置</attribute>
      <attribute name="action">app.preferences</attribute>
      <attribute name="verb-icon">preferences-system-symbolic</attribute>
    </item>
  </section>
  <section>
    <item>
      <attribute name="label">_关于</attribute>
      <attribute name="action">app.about</attribute>
    </item>
  </section>
</menu>

State Management

状态管理

GSettings for Persistent State

使用GSettings实现持久化状态

c
// Create settings
GSettings *settings = g_settings_new("org.example.app");

// Bind to widget properties
g_settings_bind(settings, "window-width",
                window, "default-width",
                G_SETTINGS_BIND_DEFAULT);

// Watch for changes
g_signal_connect(settings, "changed::theme",
                G_CALLBACK(on_theme_changed), NULL);
c
// 创建设置实例
GSettings *settings = g_settings_new("org.example.app");

// 将设置绑定到组件属性
g_settings_bind(settings, "window-width",
                window, "default-width",
                G_SETTINGS_BIND_DEFAULT);

// 监听设置变化
g_signal_connect(settings, "changed::theme",
                G_CALLBACK(on_theme_changed), NULL);

Property Bindings

属性绑定

c
// Bidirectional binding between widgets
g_object_bind_property(
    entry, "text",
    label, "label",
    G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE
);

// Transform bindings with transform functions
g_object_bind_property_full(
    slider, "value",
    label, "label",
    G_BINDING_DEFAULT,
    slider_to_label_transform,
    label_to_slider_transform,
    NULL, NULL
);
c
// 组件间双向绑定
g_object_bind_property(
    entry, "text",
    label, "label",
    G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE
);

// 使用转换函数实现转换绑定
g_object_bind_property_full(
    slider, "value",
    label, "label",
    G_BINDING_DEFAULT,
    slider_to_label_transform,
    label_to_slider_transform,
    NULL, NULL
);

Event Controllers (GTK4 Modern)

事件控制器(GTK4现代方式)

Keyboard Controller

键盘控制器

c
// Use event controllers instead of signals
GtkEventController *key_controller = gtk_event_controller_key_new();
gtk_widget_add_controller(widget, key_controller);
g_signal_connect(key_controller, "key-pressed",
                G_CALLBACK(on_key_pressed), self);
c
// 使用事件控制器替代传统信号
GtkEventController *key_controller = gtk_event_controller_key_new();
gtk_widget_add_controller(widget, key_controller);
g_signal_connect(key_controller, "key-pressed",
                G_CALLBACK(on_key_pressed), self);

Gesture Controllers

手势控制器

c
// Click gesture
GtkGesture *click = gtk_gesture_click_new();
g_signal_connect(click, "pressed", G_CALLBACK(on_click_pressed), widget);
gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(click));

// Drag gesture
GtkGesture *drag = gtk_gesture_drag_new();
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(drag), GDK_BUTTON_PRIMARY);
g_signal_connect(drag, "drag-begin", G_CALLBACK(on_drag_begin), NULL);
g_signal_connect(drag, "drag-update", G_CALLBACK(on_drag_update), NULL);
gtk_widget_add_controller(drawing_area, GTK_EVENT_CONTROLLER(drag));

c
// 点击手势
GtkGesture *click = gtk_gesture_click_new();
g_signal_connect(click, "pressed", G_CALLBACK(on_click_pressed), widget);
gtk_widget_add_controller(widget, GTK_EVENT_CONTROLLER(click));

// 拖拽手势
GtkGesture *drag = gtk_gesture_drag_new();
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(drag), GDK_BUTTON_PRIMARY);
g_signal_connect(drag, "drag-begin", G_CALLBACK(on_drag_begin), NULL);
g_signal_connect(drag, "drag-update", G_CALLBACK(on_drag_update), NULL);
gtk_widget_add_controller(drawing_area, GTK_EVENT_CONTROLLER(drag));

Anti-Patterns & Common Mistakes

反模式与常见错误

Memory Management

内存管理

WRONG: Manual reference management

错误做法:手动管理引用

c
GtkWidget *child = gtk_button_new_with_label("Click");
gtk_box_append(GTK_BOX(box), child);
g_object_unref(child);  // DANGER: May leak or crash
c
GtkWidget *child = gtk_button_new_with_label("点击");
gtk_box_append(GTK_BOX(box), child);
g_object_unref(child);  // 危险:可能导致内存泄漏或崩溃

CORRECT: Let containers manage ownership

正确做法:让容器管理所有权

c
GtkWidget *child = gtk_button_new_with_label("Click");
gtk_box_append(GTK_BOX(box), child);
// Box takes ownership - no unref needed
c
GtkWidget *child = gtk_button_new_with_label("点击");
gtk_box_append(GTK_BOX(box), child);
// 容器会接管所有权 - 无需手动释放

WRONG: Not disconnecting signals

错误做法:未断开信号连接

c
static void my_widget_init(MyWidget *self) {
    g_signal_connect(self->button, "clicked",
                     G_CALLBACK(on_clicked), self);
}
// Never disconnects - memory leak!
c
static void my_widget_init(MyWidget *self) {
    g_signal_connect(self->button, "clicked",
                     G_CALLBACK(on_clicked), self);
}
// 从未断开连接 - 内存泄漏!

CORRECT: Clean up in dispose

正确做法:在dispose中清理

c
static void my_widget_dispose(GObject *object) {
    MyWidget *self = MY_WIDGET(object);
    g_clear_signal_handler(&self->button_clicked_id, self->button);
    G_OBJECT_CLASS(my_widget_parent_class)->dispose(object);
}
c
static void my_widget_dispose(GObject *object) {
    MyWidget *self = MY_WIDGET(object);
    g_clear_signal_handler(&self->button_clicked_id, self->button);
    G_OBJECT_CLASS(my_widget_parent_class)->dispose(object);
}

Event Handling

事件处理

WRONG: Using GTK3 signals

错误做法:使用GTK3信号

c
g_signal_connect(widget, "key-press-event",
                 G_CALLBACK(old_handler), NULL);  // GTK3 pattern
c
g_signal_connect(widget, "key-press-event",
                 G_CALLBACK(old_handler), NULL);  // GTK3模式

CORRECT: Use GTK4 event controllers

正确做法:使用GTK4事件控制器

c
GtkEventController *controller = gtk_event_controller_key_new();
gtk_widget_add_controller(widget, controller);
g_signal_connect(controller, "key-pressed",
                 G_CALLBACK(modern_handler), NULL);
c
GtkEventController *controller = gtk_event_controller_key_new();
gtk_widget_add_controller(widget, controller);
g_signal_connect(controller, "key-pressed",
                 G_CALLBACK(modern_handler), NULL);

API Mixing

API混合使用

WRONG: Mixing GTK3 and GTK4 APIs

错误做法:混合GTK3与GTK4 API

c
GtkWidget *window = gtk_window_new();  // GTK3
GtkApplicationWindow *app_win = gtk_application_window_new(app);  // GTK4
c
GtkWidget *window = gtk_window_new();  // GTK3
GtkApplicationWindow *app_win = gtk_application_window_new(app);  // GTK4

CORRECT: Use consistent GTK4 APIs

正确做法:统一使用GTK4 API

c
GtkWidget *app_win = gtk_application_window_new(app);
c
GtkWidget *app_win = gtk_application_window_new(app);

Performance

性能优化

WRONG: Excessive redraws

错误做法:过度重绘

c
void on_data_changed(void) {
    gtk_widget_queue_draw(widget);  // Triggers full redraw
}
c
void on_data_changed(void) {
    gtk_widget_queue_draw(widget);  // 触发完整重绘
}

CORRECT: Use property notifications

正确做法:使用属性通知

c
void on_data_changed(void) {
    gtk_widget_notify(widget, "content");  // More efficient
}
c
void on_data_changed(void) {
    gtk_widget_notify(widget, "content");  // 更高效
}

WRONG: Rebuilding list models

错误做法:重建列表模型

c
void update_list(void) {
    gtk_list_view_set_model(listview, create_new_model());  // Slow
}
c
void update_list(void) {
    gtk_list_view_set_model(listview, create_new_model());  // 缓慢
}

CORRECT: Modify existing model

正确做法:修改现有模型

c
void update_list(void) {
    GListStore *store = get_current_store();
    g_list_store_remove(store, old_item);
    g_list_store_append(store, new_item);  // Fast
}

c
void update_list(void) {
    GListStore *store = get_current_store();
    g_list_store_remove(store, old_item);
    g_list_store_append(store, new_item);  // 快速
}

Reference Resources

参考资源

Official Documentation

官方文档

Real-World Examples

真实场景示例

Pattern References

模式参考

  • See
    references/
    directory for:
    • gnome-hig.md
      - HIG principles and patterns
    • gtk4-best-practices.md
      - Modern GTK4 code patterns
    • libadwaita-styling.md
      - CSS theming with Libadwaita
    • accessibility.md
      - A11y implementation guide

  • 查看
    references/
    目录获取:
    • gnome-hig.md
      - HIG原则与模式
    • gtk4-best-practices.md
      - 现代GTK4代码模式
    • libadwaita-styling.md
      - Libadwaita CSS主题定制
    • accessibility.md
      - 无障碍实现指南

Quick Start Example

快速入门示例

Creating a Modern GTK4 Window

创建现代GTK4窗口

c
// UI resource (window.ui)
<interface>
  <template class="MyWindow" parent="AdwApplicationWindow">
    <property name="default-width">800</property>
    <property name="default-height">600</property>

    <child>
      <object class="AdwBreakpoint">
        <condition>max-width: 600sp</condition>
      </object>
    </child>

    <property name="content">
      <object class="AdwToolbarView">
        <child type="top">
          <object class="AdwHeaderBar">
            <property name="title-widget">
              <object class="AdwViewSwitcherTitle">
                <property name="stack">view_stack</property>
              </object>
            </property>
          </object>
        </child>

        <property name="content">
          <object class="GtkStack" id="view_stack">
            <!-- Stack children added here -->
          </object>
        </property>
      </object>
    </property>
  </template>
</interface>
c
// C implementation
G_DEFINE_TYPE(MyWindow, my_window, ADW_TYPE_APPLICATION_WINDOW);

static void my_window_class_init(MyWindowClass *klass) {
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    gtk_widget_class_set_template_from_resource(widget_class,
        "/org/example/app/ui/window.ui");
}

static void my_window_init(MyWindow *self) {
    gtk_widget_init_template(GTK_WIDGET(self));

    // Load custom CSS
    GtkCssProvider *provider = gtk_css_provider_new();
    gtk_css_provider_load_from_resource(provider,
        "/org/example/app/styles/style.css");
    gtk_style_context_add_provider_for_display(
        gdk_display_get_default(),
        GTK_STYLE_PROVIDER(provider),
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
    );
}
css
/* Custom styling (style.css) */
window {
  background-color: var(--window-bg-color);
}

.title {
  font-weight: 700;
  font-size: 24pt;
}

.primary-button {
  background-color: var(--accent-bg-color);
  color: var(--accent-fg-color);
}

.primary-button:hover {
  /* GTK4: Use color-mix() instead of filter: brightness() */
  background-color: color-mix(in srgb, var(--accent-bg-color) 90%, white);
}
This example demonstrates:
  • AdwApplicationWindow for native GNOME integration
  • Header bar with view switcher
  • Custom CSS loading with GTK4 syntax
  • Responsive design with AdwBreakpoint
  • Libadwaita theming using CSS variables
c
// UI资源文件(window.ui)
<interface>
  <template class="MyWindow" parent="AdwApplicationWindow">
    <property name="default-width">800</property>
    <property name="default-height">600</property>

    <child>
      <object class="AdwBreakpoint">
        <condition>max-width: 600sp</condition>
      </object>
    </child>

    <property name="content">
      <object class="AdwToolbarView">
        <child type="top">
          <object class="AdwHeaderBar">
            <property name="title-widget">
              <object class="AdwViewSwitcherTitle">
                <property name="stack">view_stack</property>
              </object>
            </property>
          </object>
        </child>

        <property name="content">
          <object class="GtkStack" id="view_stack">
            <!-- 在此添加栈子组件 -->
          </object>
        </property>
      </object>
    </property>
  </template>
</interface>
c
// C语言实现
G_DEFINE_TYPE(MyWindow, my_window, ADW_TYPE_APPLICATION_WINDOW);

static void my_window_class_init(MyWindowClass *klass) {
    GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
    gtk_widget_class_set_template_from_resource(widget_class,
        "/org/example/app/ui/window.ui");
}

static void my_window_init(MyWindow *self) {
    gtk_widget_init_template(GTK_WIDGET(self));

    // 加载自定义CSS
    GtkCssProvider *provider = gtk_css_provider_new();
    gtk_css_provider_load_from_resource(provider,
        "/org/example/app/styles/style.css");
    gtk_style_context_add_provider_for_display(
        gdk_display_get_default(),
        GTK_STYLE_PROVIDER(provider),
        GTK_STYLE_PROVIDER_PRIORITY_APPLICATION
    );
}
css
/* 自定义样式(style.css) */
window {
  background-color: var(--window-bg-color);
}

.title {
  font-weight: 700;
  font-size: 24pt;
}

.primary-button {
  background-color: var(--accent-bg-color);
  color: var(--accent-fg-color);
}

.primary-button:hover {
  /* GTK4:使用color-mix()替代filter: brightness() */
  background-color: color-mix(in srgb, var(--accent-bg-color) 90%, white);
}
本示例展示了:
  • 用于原生GNOME集成的AdwApplicationWindow
  • 带有视图切换器的标题栏
  • 使用GTK4语法加载自定义CSS
  • 通过AdwBreakpoint实现响应式设计
  • 使用CSS变量实现Libadwaita主题定制