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 原生
| 特性 / Feature | GTK4 原生 | GTK4 + libadwaita |
|---|---|---|
| 风格 | 基础 GTK 样式 | GNOME 现代风格 |
| 自适应 | 需手动实现 | 内置 AdwBreakpoints |
| 暗色模式 | 手动切换 | 自动跟随系统 / AdwStyleManager |
| 导航 | GtkHeaderBar | AdwHeaderBar + AdwNavigationSplitView |
| 设置页面 | 需手动构建 | AdwPreferencesGroup / AdwPreferencesPage |
| Toast 通知 | 无 | AdwToastOverlay + AdwToast |
| 对话框 | GtkAlertDialog | AdwMessageDialog |
| 卡片 | 无 | 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.pillCSS class for pill-shaped buttons per GNOME HIG.
扩展阅读 / Further Reading
| 资源 / Resource | 链接 / Link |
|---|---|
| libadwaita 文档 | https://gnome.pages.gitlab.gnome.org/libadwaita/ |
| GNOME HIG | https://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 绑定 →