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"