لماذا إمكانية الوصول ليست للضعفاء، بل لجودة الكود
\n\nعندما نكتب بلغة C++، اعتدنا التفكير في الأداء والذاكرة والسرعة. ولكن هناك جانب آخر غالبًا ما يتم تجاهله — إمكانية الوصول. هذا ليس مجرد مصطلح عصري من عالم تطوير الويب. في C++، سهولة الوصول للتطبيقات تعني إتاحة الفرصة للأشخاص ذوي الإعاقات (ضعف البصر، السمع، الحركة) لاستخدام برامجك على قدم المساواة مع الآخرين.
\n\nتخيل: لقد أنشأت محرر رسوميات رائعًا أو أداة سطر أوامر. ولكن إذا لم يتمكن قارئ الشاشة من قراءتها أو لم يتم التحكم بها عبر لوحة المفاتيح — فأنت تستبعد 15-20% من المستخدمين. بالإضافة إلى ذلك، غالبًا ما تكون إمكانية الوصول مطلبًا قانونيًا (مثلًا، في الولايات المتحدة — القسم 508، في أوروبا — EN 301 549).
\n\nفي هذه المقالة، سنشرح كيفية دمج أفضل ممارسات a11y (a11y — اختصار عددي لكلمة accessibility) في مشاريع C++ دون التضحية بالأداء. هيا بنا!
\n\nمن أين نبدأ: المبادئ الأساسية للكود الشامل
\n\nقبل الخوض في الكود، تذكر الركائز الثلاث:
\n\n- \n
- الإدراك — يجب أن تكون المعلومات متاحة عبر حاسة واحدة على الأقل (البصر، السمع، اللمس). \n
- التحكم — يجب أن تعمل الواجهة مع لوحة المفاتيح، الصوت، أو المفاتيح التبديلية. \n
- الوضوح — سلوك العناصر متوقع، والأخطاء مفهومة. \n
في C++، هذا مهم بشكل خاص لأننا غالبًا ما نعمل مع واجهات برمجة تطبيقات منخفضة المستوى حيث تُفقد الدلالات. على سبيل المثال، عند رسم زر عبر OpenGL، لن تحصل على دعم تلقائي لقارئات الشاشة — بل تحتاج إلى إضافته يدويًا.
\n\nالترميز الدلالي في واجهات C++ الرسومية: 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\nإمكانية الوصول في تطبيقات سطر الأوامر C++
\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&