强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Qt 与 GTK 图形框架教程 / 09 - libadwaita 现代化 / libadwaita

libadwaita 现代化 / libadwaita Modern UI

使用 libadwaita 构建符合 GNOME HIG 的现代化、自适应桌面应用。 Build modern, responsive desktop apps with libadwaita following GNOME HIG.


9.1 libadwaita 是什么? / What is libadwaita?

libadwaita 是 GNOME 的现代 UI 库,基于 GTK4,提供符合 GNOME HIG(Human Interface Guidelines)的控件和样式。

libadwaita is GNOME’s modern UI library built on GTK4, providing HIG-compliant widgets and styling.

libadwaita vs GTK4 原生

特性 / FeatureGTK4 原生GTK4 + libadwaita
风格基础 GTK 样式GNOME 现代风格
自适应需手动实现内置 AdwBreakpoints
暗色模式手动切换自动跟随系统 / AdwStyleManager
导航GtkHeaderBarAdwHeaderBar + AdwNavigationSplitView
设置页面需手动构建AdwPreferencesGroup / AdwPreferencesPage
Toast 通知AdwToastOverlay + AdwToast
对话框GtkAlertDialogAdwMessageDialog
卡片AdwClamp + 自定义
状态页AdwStatusPage
轮播AdwCarousel
应用设置AdwApplication
# 安装 / Install
# Ubuntu/Debian
sudo apt install libadwaita-1-dev

# Fedora
sudo dnf install libadwaita-devel

# Arch
sudo pacman -S libadwaita

9.2 AdwApplication 应用结构 / Application Structure

C 完整示例 / C Complete Example

/* main.c - libadwaita 应用骨架 */
#include <adwaita.h>

static void on_activate(GtkApplication *app, gpointer user_data)
{
    /* 使用 AdwApplicationWindow 而非 GtkApplicationWindow */
    GtkWidget *window = adw_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Adwaita 示例");
    gtk_window_set_default_size(GTK_WINDOW(window), 400, 500);

    /* Toast Overlay (包裹所有内容) */
    GtkWidget *toast_overlay = adw_toast_overlay_new();

    /* 主内容容器 */
    GtkWidget *content = adw_application_window_get_content(
        ADW_APPLICATION_WINDOW(window));

    /* 使用 AdwToolbarView (libadwaita 1.4+) */
    GtkWidget *toolbar_view = adw_toolbar_view_new();

    /* Header Bar */
    GtkWidget *header = adw_header_bar_new();
    adw_header_bar_set_title_widget(ADW_HEADER_BAR(header),
        adw_window_title_new("Adwaita 示例", "子标题"));
    adw_toolbar_view_add_top_bar(ADW_TOOLBAR_VIEW(toolbar_view), header);

    /* 主内容区域 */
    GtkWidget *main_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);

    /* Status Page (欢迎页) */
    GtkWidget *status_page = adw_status_page_new();
    adw_status_page_set_icon_name(ADW_STATUS_PAGE(status_page),
                                   "face-cool-symbolic");
    adw_status_page_set_title(ADW_STATUS_PAGE(status_page),
                              "欢迎使用 Adwaita");
    adw_status_page_set_description(ADW_STATUS_PAGE(status_page),
        "使用 libadwaita 构建现代化 GNOME 应用");
    gtk_box_append(GTK_BOX(main_box), status_page);

    /* 操作按钮 */
    GtkWidget *btn_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
    gtk_widget_set_halign(btn_box, GTK_ALIGN_CENTER);

    GtkWidget *action_btn = gtk_button_new_with_label("开始使用");
    gtk_widget_add_css_class(action_btn, "suggested-action");
    gtk_widget_add_css_class(action_btn, "pill");
    gtk_box_append(GTK_BOX(btn_box), action_btn);
    gtk_box_append(GTK_BOX(main_box), btn_box);

    adw_toast_overlay_set_child(ADW_TOAST_OVERLAY(toast_overlay), main_box);
    adw_toolbar_view_set_content(ADW_TOOLBAR_VIEW(toolbar_view), toast_overlay);
    adw_application_window_set_content(
        ADW_APPLICATION_WINDOW(window), toolbar_view);

    /* 按钮点击显示 Toast */
    g_signal_connect(action_btn, "clicked", G_CALLBACK(+[](GtkButton *btn) {
        /* 获取 toast overlay */
        GtkWidget *overlay = gtk_widget_get_ancestor(
            GTK_WIDGET(btn), ADW_TYPE_TOAST_OVERLAY);
        AdwToast *toast = adw_toast_new("操作已完成!");
        adw_toast_set_timeout(toast, 2);
        adw_toast_overlay_add_toast(ADW_TOAST_OVERLAY(overlay), toast);
    }), NULL);

    gtk_window_present(GTK_WINDOW(window));
}

int main(int argc, char *argv[])
{
    g_autoptr(AdwApplication) app = adw_application_new(
        "com.example.adwaita", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(on_activate), NULL);
    return g_application_run(G_APPLICATION(app), argc, argv);
}

编译命令 / Build

gcc -o myapp main.c $(pkg-config --cflags --libs libadwaita-1) -Wall

9.3 自适应布局 / Responsive Layout

AdwNavigationSplitView(导航分割视图)

/* 自适应侧边栏:宽屏左右布局,窄屏堆叠 */
static GtkWidget *create_split_view(void)
{
    /* 创建分割视图 */
    GtkWidget *split_view = adw_navigation_split_view_new();

    /* 侧边栏页面 */
    GtkWidget *sidebar_page = adw_navigation_page_new(
        create_sidebar(), "sidebar");
    adw_navigation_split_view_set_sidebar(
        ADW_NAVIGATION_SPLIT_VIEW(split_view), sidebar_page);

    /* 内容页面 */
    GtkWidget *content_page = adw_navigation_page_new(
        create_content(), "content");
    adw_navigation_split_view_set_content(
        ADW_NAVIGATION_SPLIT_VIEW(split_view), content_page);

    /* 自动折叠:窄屏时自动隐藏侧边栏 */
    adw_navigation_split_view_set_collapsed(
        ADW_NAVIGATION_SPLIT_VIEW(split_view), TRUE);
    adw_navigation_split_view_set_min_sidebar_width(
        ADW_NAVIGATION_SPLIT_VIEW(split_view), 280.0);

    return split_view;
}

Python 自适应示例

"""libadwaita 自适应布局 - PyGObject"""

import gi
gi.require_version("Gtk", "4.0")
gi.require_version("Adw", "1")
from gi.repository import Gtk, Adw


class ResponsiveApp(Adw.Application):
    def __init__(self):
        super().__init__(application_id="com.example.responsive")
        self.connect("activate", self.on_activate)

    def on_activate(self, app):
        window = Adw.ApplicationWindow(application=app)
        window.set_title("自适应布局")
        window.set_default_size(800, 600)

        # NavigationSplitView
        split_view = Adw.NavigationSplitView()
        split_view.set_sidebar_width_fraction(0.3)
        split_view.set_collapsed(True)  # 窄屏自动折叠

        # 侧边栏
        sidebar_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
        sidebar_box.set_margin_all(8)

        for i, name in enumerate(["首页", "设置", "帮助", "关于"]):
            btn = Gtk.Button(label=name)
            btn.add_css_class("flat")
            btn.connect("clicked", lambda b, n=name: self.on_nav(n, content))
            sidebar_box.append(btn)

        sidebar_page = Adw.NavigationPage.new(sidebar_box, "侧边栏")
        split_view.set_sidebar(sidebar_page)

        # 内容区
        content = Gtk.Label(label="选择左侧导航项")
        content.set_vexpand(True)
        content.add_css_class("title-1")
        content_page = Adw.NavigationPage.new(content, "内容")
        split_view.set_content(content_page)

        window.set_content(split_view)
        window.present()

    def on_nav(self, name, label):
        label.set_text(f"当前页面: {name}")


if __name__ == "__main__":
    ResponsiveApp().run()

9.4 AdwPreferencesGroup 设置页面

/* 设置页面示例 */
static GtkWidget *create_settings_page(void)
{
    GtkWidget *page = adw_preferences_page_new();

    /* 通用设置组 */
    AdwPreferencesGroup *general = ADW_PREFERENCES_GROUP(
        adw_preferences_group_new());
    adw_preferences_group_set_title(general, "通用 / General");
    adw_preferences_page_add(ADW_PREFERENCES_PAGE(page), general);

    /* 开关行 */
    AdwSwitchRow *dark_mode = ADW_SWITCH_ROW(
        adw_switch_row_new());
    adw_preferences_row_set_title(ADW_PREFERENCES_ROW(dark_mode),
                                   "暗色模式 / Dark Mode");
    adw_action_row_set_subtitle(ADW_ACTION_ROW(dark_mode),
                                 "跟随系统或手动切换");
    adw_preferences_group_add(general, ADW_WIDGET(dark_mode));

    /* 组合行(下拉选择) */
    AdwComboRow *lang = ADW_COMBO_ROW(adw_combo_row_new());
    adw_preferences_row_set_title(ADW_PREFERENCES_ROW(lang),
                                   "语言 / Language");

    GtkStringList *lang_list = gtk_string_list_new(NULL);
    gtk_string_list_append(lang_list, "简体中文");
    gtk_string_list_append(lang_list, "English");
    gtk_string_list_append(lang_list, "日本語");
    adw_combo_row_set_model(lang, G_LIST_MODEL(lang_list));
    adw_preferences_group_add(general, ADW_WIDGET(lang));

    /* 文本输入行 */
    AdwEntryRow *username = ADW_ENTRY_ROW(adw_entry_row_new());
    adw_preferences_row_set_title(ADW_PREFERENCES_ROW(username),
                                   "用户名 / Username");
    adw_preferences_group_add(general, ADW_WIDGET(username));

    /* 通知设置组 */
    AdwPreferencesGroup *notifs = ADW_PREFERENCES_GROUP(
        adw_preferences_group_new());
    adw_preferences_group_set_title(notifs, "通知 / Notifications");
    adw_preferences_page_add(ADW_PREFERENCES_PAGE(page), notifs);

    AdwSwitchRow *push = ADW_SWITCH_ROW(adw_switch_row_new());
    adw_preferences_row_set_title(ADW_PREFERENCES_ROW(push),
                                   "推送通知 / Push");
    adw_switch_row_set_active(push, TRUE);
    adw_preferences_group_add(notifs, ADW_WIDGET(push));

    return page;
}

9.5 暗色模式 / Dark Mode

/* 程序化控制暗色模式 */
#include <adwaita.h>

/* 设置颜色方案 */
AdwStyleManager *manager = adw_style_manager_get_default();

/* 跟随系统 (默认) */
adw_style_manager_set_color_scheme(manager, ADW_COLOR_SCHEME_DEFAULT);

/* 强制亮色 */
adw_style_manager_set_color_scheme(manager, ADW_COLOR_SCHEME_FORCE_LIGHT);

/* 强制暗色 */
adw_style_manager_set_color_scheme(manager, ADW_COLOR_SCHEME_FORCE_DARK);

/* 监听变化 */
g_signal_connect(manager, "notify::color-scheme",
    G_CALLBACK(on_color_scheme_changed), NULL);
"""Python: 暗色模式控制"""
from gi.repository import Adw

manager = Adw.StyleManager.get_default()

# 强制暗色
manager.set_color_scheme(Adw.ColorScheme.FORCE_DARK)

# 强制亮色
manager.set_color_scheme(Adw.ColorScheme.FORCE_LIGHT)

# 跟随系统
manager.set_color_scheme(Adw.ColorScheme.DEFAULT)

9.6 GNOME HIG 设计原则 / GNOME HIG Principles

HIG 核心准则 / Core Guidelines

原则 / Principle说明 / Description
简洁减少界面元素,突出核心功能 / Minimize UI elements
一致性遵循 GNOME 控件使用惯例 / Follow GNOME conventions
自适应支持窗口缩放和移动端布局 / Support window resize
可达性支持屏幕阅读器和键盘导航 / Support screen readers
圆角设计大量使用圆角和间距 / Rounded corners and spacing
HeaderBar标题栏集成控件 / Title bar integrates controls

GNOME 应用设计检查清单 / Design Checklist

检查项 / Checklist状态 / Status
使用 AdwApplication + AdwApplicationWindow
HeaderBar 集成标题和操作按钮
使用 AdwPreferencesGroup 构建设置页面
支持暗色模式切换
窗口可缩放且布局自适应
使用 AdwToast 显示通知
使用 AdwMessageDialog 显示对话框
支持键盘导航和屏幕阅读器
使用 GNOME 图标命名规范
Flatpak 打包(推荐)

注意事项 / Important Notes

⚠️ libadwaita 版本 / Version Requirements

不同功能需要不同版本:

  • AdwApplication — 1.0+
  • AdwBreakpoints — 1.4+
  • AdwDialog — 1.5+
  • AdwNavigationView — 1.5+

Check libadwaita version for feature availability.

⚠️ 仅限 GNOME / GNOME Only

libadwaita 强制 GNOME 风格,在 KDE Plasma 上可能显得格格不入。 如果需要跨桌面环境通用,使用纯 GTK4。

libadwaita enforces GNOME style. Use plain GTK4 for cross-DE compatibility.

⚠️ 圆角按钮 / Rounded Buttons

GNOME HIG 推荐使用 .pill 类实现胶囊形状按钮。 Use .pill CSS class for pill-shaped buttons per GNOME HIG.


扩展阅读 / Further Reading

资源 / Resource链接 / Link
libadwaita 文档https://gnome.pages.gitlab.gnome.org/libadwaita/
GNOME HIGhttps://developer.gnome.org/hig/
GNOME 开发者中心https://developer.gnome.org/
GNOME 图标库https://gitlab.gnome.org/GNOME/adwaita-icon-theme
Flathub 指南https://docs.flathub.org/

08 - GTK4 控件 | 10 - Python 绑定