为什么无障碍化不是“弱者”的专利,而是代码质量的体现
\n\n当我们用C++编程时,我们习惯于考虑性能、内存和速度。但还有一个经常被忽略的方面——无障碍化。这不仅仅是来自Web开发领域的一个时髦术语。在C++中,应用的可访问性意味着让有障碍的人士(视力、听力、行动障碍)能够与其他人一样使用你的软件。
\n\n想象一下:你创建了一个很棒的图形编辑器或控制台工具。但如果它无法被屏幕阅读器读取,或者无法通过键盘操作——你就切断了15-20%的用户。此外,无障碍化通常是法律要求(例如,在美国是Section 508,在欧洲是EN 301 549)。
\n\n在本文中,我们将探讨如何在不牺牲性能的情况下,将a11y最佳实践(a11y是accessibility的数字缩写)融入C++项目。开始吧!
\n\n从何入手:包容性代码的基本原则
\n\n在深入代码之前,请记住三大支柱:
\n\n- \n
- 可感知性——信息应至少通过一种感官(视觉、听觉、触觉)可获取。 \n
- 可操作性——界面应能通过键盘、语音或开关设备操作。 \n
- 可理解性——元素行为可预测,错误信息清晰易懂。 \n
在C++中,这一点尤为重要,因为我们经常处理底层API,语义很容易丢失。例如,通过OpenGL绘制一个按钮,你不会自动获得屏幕阅读器的支持——需要手动添加。
\n\nC++ GUI中的语义化标记:Qt及其他
\n\n在C++中添加无障碍化最常见的方法是使用Qt框架。它通过QAccessibleInterface提供了内置支持。但即使你使用纯WinAPI,也有IAccessible可用。
示例:让按钮可访问
\n\n假设你有一个通过QPainter绘制的自定义按钮。没有无障碍化,屏幕阅读器只会看到“矩形”。我们来修复它:
\n\n#include <QAccessible>\n#include <QAccessibleWidget>\n#include <QPushButton>\n\nclass AccessibleButton : public QAccessibleWidget {\npublic:\n explicit AccessibleButton(QWidget* widget)\n : QAccessibleWidget(widget, QAccessible::Button) {}\n\n QString text(QAccessible::Text t) const override {\n if (t == QAccessible::Name) {\n auto* btn = qobject_cast<QPushButton*>(widget());\n return btn ? btn->text() : QString();\n }\n return QAccessibleWidget::text(t);\n }\n};\n\n// 使用方式:\nQPushButton* btn = new QPushButton("提交");\nQAccessible::registerAccessibleFactory(\n [](QObject* obj) -> QAccessibleInterface* {\n if (auto* btn = qobject_cast<QPushButton*>(obj)) {\n return new AccessibleButton(btn);\n }\n return nullptr;\n }\n);\n\n现在屏幕阅读器会读出“提交,按钮”。神奇吗?不,这只是包容性代码。
\n\nC++控制台应用中的无障碍化
\n\n控制台看似过时,但许多工具、CI/CD流水线和服务器应用都在终端中运行。如何让它们可访问?
\n\n为屏幕阅读器正确输出
\n\n屏幕阅读器(例如NVDA、JAWS)逐行读取控制台。如果你用ASCII字符输出表格——那将是一场噩梦。最好使用结构化输出:
\n\n#include <iostream>\n#include <string>\n#include <vector>\n\nstruct User {\n std::string name;\n int age;\n};\n\nvoid printAccessibleTable(const std::vector<User>& users) {\n // 屏幕阅读器友好:先输出标题,再输出数据\n std::cout << "用户表格:" << std::endl;\n for (const auto&