File indexing completed on 2025-01-26 03:28:31

0001 /*
0002     SPDX-FileCopyrightText: 2023-2024 Laurent Montel <montel.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 
0007 #include "uiwidget.h"
0008 
0009 #include <QPainter>
0010 using namespace QAccessibleClient;
0011 
0012 struct RoleColor {
0013     AccessibleObject::Role m_role;
0014     const char *m_color;
0015     RoleColor(AccessibleObject::Role role, const char *color)
0016         : m_role(role)
0017         , m_color(color)
0018     {
0019     }
0020 };
0021 
0022 static RoleColor roleColors[] = {
0023     {AccessibleObject::NoRole, "#ff0000"},
0024     //     { ATSPI_ROLE_ACCELERATOR_LABEL, "" },
0025     //    { ATSPI_ROLE_ALERT, "#ffacac" },
0026     ////     { ATSPI_ROLE_ANIMATION, "" },
0027     ////     { ATSPI_ROLE_ARROW, "" },
0028     ////     { ATSPI_ROLE_CALENDAR, "" },
0029     ////     { ATSPI_ROLE_CANVAS, "" },
0030     {AccessibleObject::CheckBox, "#6666ff"},
0031     {AccessibleObject::MenuItem, "#ff6666"},
0032     //    { ATSPI_ROLE_COLOR_CHOOSER, "#6666ff" },
0033     ////     { ATSPI_ROLE_COLUMN_HEADER, "" },
0034     {AccessibleObject::ComboBox, "#6666ff"},
0035     //    { ATSPI_ROLE_DATE_EDITOR, "#6666ff" },
0036     ////     { ATSPI_ROLE_DESKTOP_ICON, "" },
0037     {AccessibleObject::DesktopFrame, "#c0c0c0"},
0038     {AccessibleObject::Dial, "#6666ff"},
0039     {AccessibleObject::Dialog, "#c0c0c0"},
0040     ////     { ATSPI_ROLE_DIRECTORY_PANE, "" },
0041     ////     { ATSPI_ROLE_DRAWING_AREA, "" },
0042     //    { ATSPI_ROLE_FILE_CHOOSER, "#6666ff" },
0043     ////     { ATSPI_ROLE_FILLER, "" },
0044     ////     { ATSPI_ROLE_FOCUS_TRAVERSABLE, "" },
0045     //    { ATSPI_ROLE_FONT_CHOOSER, "#6666ff" },
0046     {AccessibleObject::Frame, "#c0c0c0"},
0047     ////     { ATSPI_ROLE_GLASS_PANE, "" },
0048     ////     { ATSPI_ROLE_HTML_CONTAINER, "" },
0049     ////     { ATSPI_ROLE_ICON, "" },
0050     ////     { ATSPI_ROLE_IMAGE, "" },
0051     //    { ATSPI_ROLE_INTERNAL_FRAME, "#c0c0c0" },
0052     //    { ATSPI_ROLE_LABEL, "#ffffaa" },
0053     ////     { ATSPI_ROLE_LAYERED_PANE, "" },
0054     {AccessibleObject::ListView, "#acacff"},
0055     {AccessibleObject::ListItem, "#6666ff"},
0056     {AccessibleObject::Menu, "#acacff"},
0057     {AccessibleObject::MenuBar, "#acacff"},
0058     {AccessibleObject::MenuItem, "#6666ff"},
0059     ////     { ATSPI_ROLE_OPTION_PANE, "" },
0060     {AccessibleObject::Tab, "#acacff"},
0061     {AccessibleObject::TabContainer, "#6666ff"},
0062     //    { AccessibleObject::Panel, "#c0c0c0" },
0063     {AccessibleObject::PasswordText, "#ffffaa"},
0064     {AccessibleObject::PopupMenu, "#acacff"},
0065     ////     { ATSPI_ROLE_PROGRESS_BAR, "" },
0066     {AccessibleObject::Button, "#6666ff"},
0067     {AccessibleObject::RadioButton, "#6666ff"},
0068     {AccessibleObject::RadioMenuItem, "#6666ff"},
0069     //    { ATSPI_ROLE_ROOT_PANE, "#c0c0c0" },
0070     ////     { ATSPI_ROLE_ROW_HEADER, "" },
0071     ////     { ATSPI_ROLE_SCROLL_BAR, "" },
0072     ////     { ATSPI_ROLE_SCROLL_PANE, "" },
0073     {AccessibleObject::Separator, "#acacff"},
0074     {AccessibleObject::Slider, "#6666ff"},
0075     {AccessibleObject::SpinButton, "#6666ff"},
0076     ////     { ATSPI_ROLE_SPLIT_PANE, "" },
0077     {AccessibleObject::StatusBar, "#c0c0c0"},
0078     {AccessibleObject::TableView, "#acacff"},
0079     {AccessibleObject::TableCell, "#6666ff"},
0080     {AccessibleObject::TableColumnHeader, "#3333ff"},
0081     {AccessibleObject::TableRowHeader, "#3333ff"},
0082     //    { ATSPI_ROLE_TEAROFF_MENU_ITEM, "#6666ff" },
0083     {AccessibleObject::Terminal, "#6666ff"},
0084     {AccessibleObject::Text, "#ffffaa"},
0085     {AccessibleObject::ToggleButton, "#6666ff"},
0086     //     { ATSPI_ROLE_TOOL_BAR, "" },
0087     {AccessibleObject::ToolTip, "#ffffaa"},
0088     {AccessibleObject::TreeView, "#acacff"},
0089     //    { ATSPI_ROLE_TREE_TABLE, "#acacff" },
0090     //     { ATSPI_ROLE_UNKNOWN, "" },
0091     //    { ATSPI_ROLE_VIEWPORT, "#c0c0c0" },
0092     {AccessibleObject::Window, "#c0c0c0"},
0093     //     { ATSPI_ROLE_EXTENDED, "" },
0094     //     { ATSPI_ROLE_HEADER, "" },
0095     //     { ATSPI_ROLE_FOOTER, "" },
0096     //    { ATSPI_ROLE_PARAGRAPH, "#ffffaa" },
0097     //     { ATSPI_ROLE_RULER, "" },
0098     //     { ATSPI_ROLE_APPLICATION, "" },
0099     //     { ATSPI_ROLE_AUTOCOMPLETE, "" },
0100     //    { ATSPI_ROLE_EDITBAR, "#6666ff" },
0101     //     { ATSPI_ROLE_EMBEDDED, "" },
0102     //     { ATSPI_ROLE_ENTRY, "" },
0103     //     { ATSPI_ROLE_CHART, "" },
0104     //    { ATSPI_ROLE_CAPTION, "#ffffaa" },
0105     //     { ATSPI_ROLE_DOCUMENT_FRAME, "" },
0106     //     { ATSPI_ROLE_HEADING, "" },
0107     //     { ATSPI_ROLE_PAGE, "" },
0108     //    { ATSPI_ROLE_SECTION, "#ffffaa" },
0109     //     { ATSPI_ROLE_REDUNDANT_OBJECT, "" },
0110     //     { ATSPI_ROLE_FORM, "" },
0111     //    { ATSPI_ROLE_LINK, "#6666ff" },
0112     //     { ATSPI_ROLE_INPUT_METHOD_WINDOW, "" },
0113     {AccessibleObject::TableRow, "#6666ff"},
0114     {AccessibleObject::TableCell, "#6666ff"},
0115     //     { ATSPI_ROLE_DOCUMENT_SPREADSHEET, "" },
0116     //     { ATSPI_ROLE_DOCUMENT_PRESENTATION, "" },
0117     //    { ATSPI_ROLE_DOCUMENT_TEXT, "#ffffaa" },
0118     //    { ATSPI_ROLE_DOCUMENT_WEB, "#ffffaa" },
0119     //    { ATSPI_ROLE_DOCUMENT_EMAIL, "#ffffaa" },
0120     //    { ATSPI_ROLE_COMMENT, "#ffffaa" },
0121     //    { ATSPI_ROLE_LIST_BOX, "#6666ff" },
0122     ////     { ATSPI_ROLE_GROUPING, "" },
0123     ////     { ATSPI_ROLE_IMAGE_MAP, "" },
0124     ////     { ATSPI_ROLE_NOTIFICATION, "" },
0125     //    { ATSPI_ROLE_INFO_BAR, "#ffffaa" },
0126     //    { ATSPI_ROLE_LAST_DEFINED, "" }
0127 };
0128 
0129 #define ROLECOLORSCOUNT (sizeof(roleColors) / (sizeof(roleColors[0])))
0130 #define ROLECOLOR "#aaffff"
0131 
0132 UiWidget::UiWidget(QWidget *view)
0133     : QWidget(view)
0134 {
0135     QPalette p = view->palette();
0136     p.setColor(QPalette::Window, Qt::white);
0137     view->setPalette(p);
0138     view->setAutoFillBackground(true);
0139 
0140     for (size_t i = 0; i < ROLECOLORSCOUNT; ++i) {
0141         RoleColor &rc = roleColors[i];
0142         m_roleColors[rc.m_role] = rc.m_color;
0143     }
0144 }
0145 
0146 void UiWidget::setAccessibleObject(const QAccessibleClient::AccessibleObject &acc)
0147 {
0148     delete m_image;
0149     m_image = nullptr;
0150     m_object = acc;
0151     m_screen = QPixmap();
0152     m_bounds = bounds(acc);
0153 
0154     adjustSize();
0155     updateGeometry();
0156     update();
0157 }
0158 
0159 QSize UiWidget::sizeHint() const
0160 {
0161     return m_bounds.size();
0162 }
0163 
0164 void UiWidget::paintEvent(QPaintEvent *event)
0165 {
0166     Q_UNUSED(event);
0167     if (!m_image) {
0168         if (m_bounds.isNull())
0169             return;
0170         m_image = new QImage(m_bounds.size(), QImage::Format_ARGB32_Premultiplied);
0171         QPainter painter(m_image);
0172         // painter.setClipRect(QRect(QPoint(0,0), QSize(m_bounds.size())));
0173 
0174         if (m_screen.isNull())
0175             m_screen = grabScreen();
0176         painter.setOpacity(0.6);
0177         painter.drawPixmap(0, 0, m_screen);
0178         painter.setOpacity(1.0);
0179 
0180         drawObject(&painter, m_object);
0181     }
0182     QPainter painter(this);
0183     painter.drawImage(0, 0, *m_image);
0184     painter.end();
0185 }
0186 
0187 QPixmap UiWidget::grabScreen()
0188 {
0189     if (m_bounds.isNull())
0190         return {};
0191     Q_ASSERT(m_bounds.left() < m_bounds.right() && m_bounds.top() < m_bounds.bottom());
0192     QPixmap pm = grab(QRect(m_bounds.x(), m_bounds.y(), m_bounds.width(), m_bounds.height()));
0193     return pm;
0194 }
0195 
0196 QRect UiWidget::bounds(const QAccessibleClient::AccessibleObject &acc) const
0197 {
0198     if (!acc.isValid())
0199         return {};
0200     QRect rect;
0201     AccessibleObject::Interfaces ifaces = acc.supportedInterfaces();
0202     if (!(ifaces & AccessibleObject::ApplicationInterface)) {
0203         QAccessibleClient::AccessibleObject parent = acc.parent();
0204         rect = bounds(parent);
0205     }
0206     if (ifaces & AccessibleObject::ComponentInterface) {
0207         QRect r = acc.boundingRect();
0208         if (!r.isNull())
0209             rect = rect.isNull() ? r : rect.united(r);
0210     }
0211     return rect;
0212 }
0213 
0214 void UiWidget::drawObject(QPainter *painter, const QAccessibleClient::AccessibleObject &acc, int depth)
0215 {
0216     if (!acc.isValid())
0217         return;
0218     ++depth;
0219     AccessibleObject::Interfaces ifaces = acc.supportedInterfaces();
0220     if (!(ifaces & AccessibleObject::ApplicationInterface)) {
0221         QAccessibleClient::AccessibleObject parent = acc.parent();
0222         drawObject(painter, parent, depth);
0223     }
0224     if (ifaces & AccessibleObject::ComponentInterface) {
0225         QRect r = acc.boundingRect();
0226         if (!r.isNull()) {
0227             r.moveTopLeft(r.topLeft() - m_bounds.topLeft());
0228 
0229             QColor color;
0230             QMap<AccessibleObject::Role, const char *>::ConstIterator colorIt = m_roleColors.constFind(acc.role());
0231             if (colorIt != m_roleColors.constEnd()) {
0232                 color = QColor(colorIt.value());
0233             } else {
0234                 color = QColor(ROLECOLOR);
0235             }
0236             color.setAlphaF(0.3);
0237 
0238             painter->fillRect(r, color);
0239             if (depth == 1) {
0240                 color = QColor(Qt::red);
0241             } else {
0242                 color = color.darker();
0243             }
0244             painter->setPen(color);
0245             painter->drawRect(r);
0246         }
0247     }
0248 }
0249 
0250 #include "moc_uiwidget.cpp"