mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-05 00:35:27 +08:00
qt 6.8.0 support
This commit is contained in:
@ -7,7 +7,6 @@
|
||||
#include "qwindowswindow.h"
|
||||
#include "qwindowskeymapper.h"
|
||||
#include "qwindowsnativeinterface.h"
|
||||
#include "qwindowsmousehandler.h"
|
||||
#include "qwindowspointerhandler.h"
|
||||
#include "qtwindowsglobal.h"
|
||||
#include "qwindowsmenu.h"
|
||||
@ -215,7 +214,6 @@ struct QWindowsContextPrivate {
|
||||
HDC m_displayContext = nullptr;
|
||||
int m_defaultDPI = 96;
|
||||
QWindowsKeyMapper m_keyMapper;
|
||||
QWindowsMouseHandler m_mouseHandler;
|
||||
QWindowsPointerHandler m_pointerHandler;
|
||||
QWindowsMimeRegistry m_mimeConverter;
|
||||
QWindowsScreenManager m_screenManager;
|
||||
@ -228,11 +226,9 @@ struct QWindowsContextPrivate {
|
||||
bool m_asyncExpose = false;
|
||||
HPOWERNOTIFY m_powerNotification = nullptr;
|
||||
HWND m_powerDummyWindow = nullptr;
|
||||
static bool m_darkMode;
|
||||
static bool m_v2DpiAware;
|
||||
};
|
||||
|
||||
bool QWindowsContextPrivate::m_darkMode = false;
|
||||
bool QWindowsContextPrivate::m_v2DpiAware = false;
|
||||
|
||||
QWindowsContextPrivate::QWindowsContextPrivate()
|
||||
@ -241,7 +237,7 @@ QWindowsContextPrivate::QWindowsContextPrivate()
|
||||
QWindowsContext::user32dll.init();
|
||||
QWindowsContext::shcoredll.init();
|
||||
|
||||
if (m_pointerHandler.touchDevice() || m_mouseHandler.touchDevice())
|
||||
if (m_pointerHandler.touchDevice())
|
||||
m_systemInfo |= QWindowsContext::SI_SupportsTouch;
|
||||
m_displayContext = GetDC(nullptr);
|
||||
m_defaultDPI = GetDeviceCaps(m_displayContext, LOGPIXELSY);
|
||||
@ -249,7 +245,6 @@ QWindowsContextPrivate::QWindowsContextPrivate()
|
||||
m_systemInfo |= QWindowsContext::SI_RTL_Extensions;
|
||||
m_keyMapper.setUseRTLExtensions(true);
|
||||
}
|
||||
m_darkMode = QWindowsTheme::queryDarkMode();
|
||||
if (FAILED(m_oleInitializeResult)) {
|
||||
qWarning() << "QWindowsContext: OleInitialize() failed: "
|
||||
<< QSystemError::windowsComString(m_oleInitializeResult);
|
||||
@ -306,8 +301,7 @@ bool QWindowsContext::initTouch(unsigned integrationOptions)
|
||||
{
|
||||
if (d->m_systemInfo & QWindowsContext::SI_SupportsTouch)
|
||||
return true;
|
||||
const bool usePointerHandler = (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) != 0;
|
||||
auto touchDevice = usePointerHandler ? d->m_pointerHandler.touchDevice() : d->m_mouseHandler.touchDevice();
|
||||
auto touchDevice = d->m_pointerHandler.touchDevice();
|
||||
if (touchDevice.isNull()) {
|
||||
const bool mouseEmulation =
|
||||
(integrationOptions & QWindowsIntegration::DontPassOsMouseEventsSynthesizedFromTouch) == 0;
|
||||
@ -316,7 +310,6 @@ bool QWindowsContext::initTouch(unsigned integrationOptions)
|
||||
if (touchDevice.isNull())
|
||||
return false;
|
||||
d->m_pointerHandler.setTouchDevice(touchDevice);
|
||||
d->m_mouseHandler.setTouchDevice(touchDevice);
|
||||
QWindowSystemInterface::registerInputDevice(touchDevice.data());
|
||||
|
||||
d->m_systemInfo |= QWindowsContext::SI_SupportsTouch;
|
||||
@ -356,21 +349,6 @@ bool QWindowsContext::disposeTablet()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool QWindowsContext::initPointer(unsigned integrationOptions)
|
||||
{
|
||||
if (integrationOptions & QWindowsIntegration::DontUseWMPointer)
|
||||
return false;
|
||||
|
||||
if (QOperatingSystemVersion::current() < QOperatingSystemVersion::Windows8)
|
||||
return false;
|
||||
|
||||
if (!QWindowsContext::user32dll.supportsPointerApi())
|
||||
return false;
|
||||
|
||||
d->m_systemInfo |= QWindowsContext::SI_SupportsPointer;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern "C" LRESULT QT_WIN_CALLBACK qWindowsPowerWindowProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
if (message != WM_POWERBROADCAST || wParam != PBT_POWERSETTINGCHANGE)
|
||||
@ -585,11 +563,6 @@ bool QWindowsContext::setProcessDpiAwareness(QtWindows::DpiAwareness dpiAwarenes
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QWindowsContext::isDarkMode()
|
||||
{
|
||||
return QWindowsContextPrivate::m_darkMode;
|
||||
}
|
||||
|
||||
QWindowsContext *QWindowsContext::instance()
|
||||
{
|
||||
return m_instance;
|
||||
@ -857,16 +830,12 @@ QWindow *QWindowsContext::findWindow(HWND hwnd) const
|
||||
|
||||
QWindow *QWindowsContext::windowUnderMouse() const
|
||||
{
|
||||
return (d->m_systemInfo & QWindowsContext::SI_SupportsPointer) ?
|
||||
d->m_pointerHandler.windowUnderMouse() : d->m_mouseHandler.windowUnderMouse();
|
||||
return d->m_pointerHandler.windowUnderMouse();
|
||||
}
|
||||
|
||||
void QWindowsContext::clearWindowUnderMouse()
|
||||
{
|
||||
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
|
||||
d->m_pointerHandler.clearWindowUnderMouse();
|
||||
else
|
||||
d->m_mouseHandler.clearWindowUnderMouse();
|
||||
d->m_pointerHandler.clearWindowUnderMouse();
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -1130,9 +1099,10 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
||||
|
||||
MSG msg;
|
||||
msg.hwnd = hwnd; // re-create MSG structure
|
||||
msg.message = message; // time and pt fields ignored
|
||||
msg.message = message;
|
||||
msg.wParam = wParam;
|
||||
msg.lParam = lParam;
|
||||
msg.time = GetMessageTime();
|
||||
msg.pt.x = msg.pt.y = 0;
|
||||
if (et != QtWindows::CursorEvent && (et & (QtWindows::MouseEventFlag | QtWindows::NonClientEventFlag))) {
|
||||
msg.pt.x = GET_X_LPARAM(lParam);
|
||||
@ -1181,8 +1151,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
||||
|
||||
switch (et) {
|
||||
case QtWindows::GestureEvent:
|
||||
if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
|
||||
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateGestureEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
// TODO???
|
||||
break;
|
||||
case QtWindows::InputMethodOpenCandidateWindowEvent:
|
||||
case QtWindows::InputMethodCloseCandidateWindowEvent:
|
||||
@ -1216,21 +1185,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
||||
// Only refresh the window theme if the user changes the personalize settings.
|
||||
if ((wParam == 0) && (lParam != 0) // lParam sometimes may be NULL.
|
||||
&& (wcscmp(reinterpret_cast<LPCWSTR>(lParam), L"ImmersiveColorSet") == 0)) {
|
||||
const bool darkMode = QWindowsTheme::queryDarkMode();
|
||||
const bool darkModeChanged = darkMode != QWindowsContextPrivate::m_darkMode;
|
||||
QWindowsContextPrivate::m_darkMode = darkMode;
|
||||
auto integration = QWindowsIntegration::instance();
|
||||
integration->updateApplicationBadge();
|
||||
if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
|
||||
QWindowsTheme::instance()->refresh();
|
||||
QWindowSystemInterface::handleThemeChange();
|
||||
}
|
||||
if (darkModeChanged) {
|
||||
if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames)) {
|
||||
for (QWindowsWindow *w : d->m_windows)
|
||||
w->setDarkBorder(QWindowsContextPrivate::m_darkMode);
|
||||
}
|
||||
}
|
||||
QWindowsTheme::handleSettingsChanged();
|
||||
}
|
||||
return d->m_screenManager.handleScreenChanges();
|
||||
}
|
||||
@ -1336,16 +1291,11 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
||||
case QtWindows::NonClientMouseEvent:
|
||||
if (!platformWindow->frameStrutEventsEnabled())
|
||||
break;
|
||||
if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
else
|
||||
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
case QtWindows::NonClientPointerEvent:
|
||||
if (!platformWindow->frameStrutEventsEnabled())
|
||||
break;
|
||||
if ((d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
break;
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
case QtWindows::EnterSizeMoveEvent:
|
||||
platformWindow->setFlag(QWindowsWindow::ResizeMoveActive);
|
||||
if (!IsZoomed(hwnd))
|
||||
@ -1359,8 +1309,7 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
||||
platformWindow->updateRestoreGeometry();
|
||||
return true;
|
||||
case QtWindows::ScrollEvent:
|
||||
if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
|
||||
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateScrollEvent(platformWindow->window(), hwnd, msg, result);
|
||||
// TODO???
|
||||
break;
|
||||
case QtWindows::MouseWheelEvent:
|
||||
case QtWindows::MouseEvent:
|
||||
@ -1371,20 +1320,14 @@ bool QWindowsContext::windowsProc(HWND hwnd, UINT message,
|
||||
window = window->parent();
|
||||
if (!window)
|
||||
return false;
|
||||
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
|
||||
else
|
||||
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateMouseEvent(window, hwnd, et, msg, result);
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translateMouseEvent(window, hwnd, et, msg, result);
|
||||
}
|
||||
break;
|
||||
case QtWindows::TouchEvent:
|
||||
if (!(d->m_systemInfo & QWindowsContext::SI_SupportsPointer))
|
||||
return sessionManagerInteractionBlocked() || d->m_mouseHandler.translateTouchEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
// TODO???
|
||||
break;
|
||||
case QtWindows::PointerEvent:
|
||||
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
break;
|
||||
return sessionManagerInteractionBlocked() || d->m_pointerHandler.translatePointerEvent(platformWindow->window(), hwnd, et, msg, result);
|
||||
case QtWindows::FocusInEvent: // see QWindowsWindow::requestActivateWindow().
|
||||
case QtWindows::FocusOutEvent:
|
||||
handleFocusEvent(et, platformWindow);
|
||||
@ -1588,7 +1531,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window)
|
||||
// Mouse is left in pressed state after press on size grip (inside window),
|
||||
// no further mouse events are received
|
||||
// For cases 1,3, intercept WM_EXITSIZEMOVE to sync the buttons.
|
||||
const Qt::MouseButtons currentButtons = QWindowsMouseHandler::queryMouseButtons();
|
||||
const Qt::MouseButtons currentButtons = QWindowsPointerHandler::queryMouseButtons();
|
||||
const Qt::MouseButtons appButtons = QGuiApplication::mouseButtons();
|
||||
if (currentButtons == appButtons)
|
||||
return;
|
||||
@ -1604,10 +1547,7 @@ void QWindowsContext::handleExitSizeMove(QWindow *window)
|
||||
currentButtons, button, type, keyboardModifiers);
|
||||
}
|
||||
}
|
||||
if (d->m_systemInfo & QWindowsContext::SI_SupportsPointer)
|
||||
d->m_pointerHandler.clearEvents();
|
||||
else
|
||||
d->m_mouseHandler.clearEvents();
|
||||
d->m_pointerHandler.clearEvents();
|
||||
}
|
||||
|
||||
bool QWindowsContext::asyncExpose() const
|
||||
|
@ -143,8 +143,7 @@ public:
|
||||
enum SystemInfoFlags
|
||||
{
|
||||
SI_RTL_Extensions = 0x1,
|
||||
SI_SupportsTouch = 0x2,
|
||||
SI_SupportsPointer = 0x4,
|
||||
SI_SupportsTouch = 0x2
|
||||
};
|
||||
|
||||
// Verbose flag set by environment variable QT_QPA_VERBOSE
|
||||
@ -157,7 +156,6 @@ public:
|
||||
bool initTouch(unsigned integrationOptions); // For calls from QWindowsIntegration::QWindowsIntegration() only.
|
||||
void registerTouchWindows();
|
||||
bool initTablet();
|
||||
bool initPointer(unsigned integrationOptions);
|
||||
bool disposeTablet();
|
||||
|
||||
bool initPowerNotificationHandler();
|
||||
@ -210,8 +208,6 @@ public:
|
||||
static QtWindows::DpiAwareness processDpiAwareness();
|
||||
static QtWindows::DpiAwareness windowDpiAwareness(HWND hwnd);
|
||||
|
||||
static bool isDarkMode();
|
||||
|
||||
void setDetectAltGrModifier(bool a);
|
||||
|
||||
// Returns a combination of SystemInfoFlags
|
||||
|
@ -11,7 +11,7 @@
|
||||
#include "qwindowsintegration.h"
|
||||
#include "qwindowsdropdataobject.h"
|
||||
#include "qwindowswindow.h"
|
||||
#include "qwindowsmousehandler.h"
|
||||
#include "qwindowspointerhandler.h"
|
||||
#include "qwindowscursor.h"
|
||||
#include "qwindowskeymapper.h"
|
||||
|
||||
@ -344,7 +344,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
|
||||
// In some rare cases, when a mouse button is released but the mouse is static,
|
||||
// grfKeyState will not be updated with these released buttons until the mouse
|
||||
// is moved. So we use the async key state given by queryMouseButtons() instead.
|
||||
Qt::MouseButtons buttons = QWindowsMouseHandler::queryMouseButtons();
|
||||
Qt::MouseButtons buttons = QWindowsPointerHandler::queryMouseButtons();
|
||||
|
||||
SCODE result = S_OK;
|
||||
if (fEscapePressed || QWindowsDrag::isCanceled()) {
|
||||
@ -369,7 +369,7 @@ QWindowsOleDropSource::QueryContinueDrag(BOOL fEscapePressed, DWORD grfKeyState)
|
||||
const QPoint localPos = m_windowUnderMouse->handle()->mapFromGlobal(globalPos);
|
||||
QWindowSystemInterface::handleMouseEvent(m_windowUnderMouse.data(),
|
||||
QPointF(localPos), QPointF(globalPos),
|
||||
QWindowsMouseHandler::queryMouseButtons(),
|
||||
QWindowsPointerHandler::queryMouseButtons(),
|
||||
Qt::LeftButton, QEvent::MouseButtonRelease);
|
||||
}
|
||||
m_currentButtons = Qt::NoButton;
|
||||
@ -463,7 +463,7 @@ void QWindowsOleDropTarget::handleDrag(QWindow *window, DWORD grfKeyState,
|
||||
const Qt::DropActions actions = translateToQDragDropActions(*pdwEffect);
|
||||
|
||||
lastModifiers = toQtKeyboardModifiers(grfKeyState);
|
||||
lastButtons = QWindowsMouseHandler::queryMouseButtons();
|
||||
lastButtons = QWindowsPointerHandler::queryMouseButtons();
|
||||
|
||||
const QPlatformDragQtResponse response =
|
||||
QWindowSystemInterface::handleDrag(window, windowsDrag->dropData(),
|
||||
@ -533,7 +533,7 @@ QWindowsOleDropTarget::DragLeave()
|
||||
|
||||
const auto *keyMapper = QWindowsContext::instance()->keyMapper();
|
||||
lastModifiers = keyMapper->queryKeyboardModifiers();
|
||||
lastButtons = QWindowsMouseHandler::queryMouseButtons();
|
||||
lastButtons = QWindowsPointerHandler::queryMouseButtons();
|
||||
|
||||
QWindowSystemInterface::handleDrag(m_window, nullptr, QPoint(), Qt::IgnoreAction,
|
||||
Qt::NoButton, Qt::NoModifier);
|
||||
@ -562,7 +562,7 @@ QWindowsOleDropTarget::Drop(LPDATAOBJECT pDataObj, DWORD grfKeyState,
|
||||
QWindowsDrag *windowsDrag = QWindowsDrag::instance();
|
||||
|
||||
lastModifiers = toQtKeyboardModifiers(grfKeyState);
|
||||
lastButtons = QWindowsMouseHandler::queryMouseButtons();
|
||||
lastButtons = QWindowsPointerHandler::queryMouseButtons();
|
||||
|
||||
const QPlatformDropQtResponse response =
|
||||
QWindowSystemInterface::handleDrop(m_window, windowsDrag->dropData(),
|
||||
|
@ -806,7 +806,7 @@ static void showSystemMenu(QWindow* w)
|
||||
qWindowsWndProc(topLevelHwnd, WM_SYSCOMMAND, WPARAM(ret), 0);
|
||||
}
|
||||
|
||||
static inline void sendExtendedPressRelease(QWindow *w, int k,
|
||||
static inline void sendExtendedPressRelease(QWindow *w, unsigned long timestamp, int k,
|
||||
Qt::KeyboardModifiers mods,
|
||||
quint32 nativeScanCode,
|
||||
quint32 nativeVirtualKey,
|
||||
@ -815,8 +815,8 @@ static inline void sendExtendedPressRelease(QWindow *w, int k,
|
||||
bool autorep = false,
|
||||
ushort count = 1)
|
||||
{
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(w, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyPress, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(w, timestamp, QEvent::KeyRelease, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count);
|
||||
}
|
||||
|
||||
/*!
|
||||
@ -883,7 +883,7 @@ bool QWindowsKeyMapper::translateMultimediaKeyEventInternal(QWindow *window, con
|
||||
|
||||
const int qtKey = int(CmdTbl[cmd]);
|
||||
if (!skipPressRelease)
|
||||
sendExtendedPressRelease(receiver, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
|
||||
sendExtendedPressRelease(receiver, msg.time, qtKey, Qt::KeyboardModifier(state), 0, 0, 0);
|
||||
// QTBUG-43343: Make sure to return false if Qt does not handle the key, otherwise,
|
||||
// the keys are not passed to the active media player.
|
||||
# if QT_CONFIG(shortcut)
|
||||
@ -963,7 +963,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
||||
// A multi-character key or a Input method character
|
||||
// not found by our look-ahead
|
||||
if (msgType == WM_CHAR || msgType == WM_IME_CHAR) {
|
||||
sendExtendedPressRelease(receiver, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false);
|
||||
sendExtendedPressRelease(receiver, msg.time, 0, Qt::KeyboardModifier(state), scancode, 0, nModifiers, messageKeyText(msg), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -998,14 +998,14 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
||||
if (dirStatus == VK_LSHIFT
|
||||
&& ((msg.wParam == VK_SHIFT && GetKeyState(VK_LCONTROL))
|
||||
|| (msg.wParam == VK_CONTROL && GetKeyState(VK_LSHIFT)))) {
|
||||
sendExtendedPressRelease(receiver, Qt::Key_Direction_L, {},
|
||||
sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_L, {},
|
||||
scancode, vk_key, nModifiers, QString(), false);
|
||||
result = true;
|
||||
dirStatus = 0;
|
||||
} else if (dirStatus == VK_RSHIFT
|
||||
&& ( (msg.wParam == VK_SHIFT && GetKeyState(VK_RCONTROL))
|
||||
|| (msg.wParam == VK_CONTROL && GetKeyState(VK_RSHIFT)))) {
|
||||
sendExtendedPressRelease(receiver, Qt::Key_Direction_R, {},
|
||||
sendExtendedPressRelease(receiver, msg.time, Qt::Key_Direction_R, {},
|
||||
scancode, vk_key, nModifiers, QString(), false);
|
||||
result = true;
|
||||
dirStatus = 0;
|
||||
@ -1218,9 +1218,9 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
||||
// so, we have an auto-repeating key
|
||||
if (rec) {
|
||||
if (code < Qt::Key_Shift || code > Qt::Key_ScrollLock) {
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code,
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
|
||||
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code,
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
|
||||
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam), nModifiers, rec->text, true);
|
||||
result = true;
|
||||
}
|
||||
@ -1246,7 +1246,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
||||
if (msg.wParam == VK_PACKET)
|
||||
code = asciiToKeycode(char(uch.cell()), state);
|
||||
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code,
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
|
||||
modifiers, scancode, quint32(msg.wParam), nModifiers, text, false);
|
||||
result =true;
|
||||
bool store = true;
|
||||
@ -1288,10 +1288,10 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
||||
if ((msg.lParam & 0x40000000) == 0 &&
|
||||
Qt::KeyboardModifier(state) == Qt::NoModifier &&
|
||||
((code == Qt::Key_F18) || (code == Qt::Key_F19) || (code == Qt::Key_F20))) {
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyPress, code,
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyPress, code,
|
||||
Qt::MetaModifier, scancode,
|
||||
quint32(msg.wParam), MetaLeft);
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code,
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
|
||||
Qt::NoModifier, scancode,
|
||||
quint32(msg.wParam), 0);
|
||||
result = true;
|
||||
@ -1303,7 +1303,7 @@ bool QWindowsKeyMapper::translateKeyEventInternal(QWindow *window, MSG msg,
|
||||
// Map SHIFT + Tab to SHIFT + BackTab, QShortcutMap knows about this translation
|
||||
if (code == Qt::Key_Tab && (state & Qt::ShiftModifier) == Qt::ShiftModifier)
|
||||
code = Qt::Key_Backtab;
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, QEvent::KeyRelease, code,
|
||||
QWindowSystemInterface::handleExtendedKeyEvent(receiver, msg.time, QEvent::KeyRelease, code,
|
||||
Qt::KeyboardModifier(state), scancode, quint32(msg.wParam),
|
||||
nModifiers,
|
||||
(rec ? rec->text : QString()), false);
|
||||
|
@ -4,7 +4,6 @@
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
#include "qwindowspointerhandler.h"
|
||||
#include "qwindowsmousehandler.h"
|
||||
#if QT_CONFIG(tabletevent)
|
||||
# include "qwindowstabletsupport.h"
|
||||
#endif
|
||||
@ -41,6 +40,14 @@ enum {
|
||||
|
||||
qint64 QWindowsPointerHandler::m_nextInputDeviceId = 1;
|
||||
|
||||
const QPointingDevice *primaryMouse()
|
||||
{
|
||||
static QPointer<const QPointingDevice> result;
|
||||
if (!result)
|
||||
result = QPointingDevice::primaryPointingDevice();
|
||||
return result;
|
||||
}
|
||||
|
||||
QWindowsPointerHandler::~QWindowsPointerHandler()
|
||||
{
|
||||
}
|
||||
@ -240,7 +247,7 @@ static Qt::MouseButtons mouseButtonsFromKeyState(WPARAM keyState)
|
||||
return result;
|
||||
}
|
||||
|
||||
static Qt::MouseButtons queryMouseButtons()
|
||||
Qt::MouseButtons QWindowsPointerHandler::queryMouseButtons()
|
||||
{
|
||||
Qt::MouseButtons result = Qt::NoButton;
|
||||
const bool mouseSwapped = GetSystemMetrics(SM_SWAPBUTTON);
|
||||
@ -454,7 +461,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
|
||||
|
||||
if (msg.message == WM_POINTERCAPTURECHANGED) {
|
||||
const auto *keyMapper = QWindowsContext::instance()->keyMapper();
|
||||
QWindowSystemInterface::handleTouchCancelEvent(window, m_touchDevice.data(),
|
||||
QWindowSystemInterface::handleTouchCancelEvent(window, msg.time, m_touchDevice.data(),
|
||||
keyMapper->queryKeyboardModifiers());
|
||||
m_lastTouchPoints.clear();
|
||||
return true;
|
||||
@ -569,7 +576,7 @@ bool QWindowsPointerHandler::translateTouchEvent(QWindow *window, HWND hwnd,
|
||||
m_touchInputIDToTouchPointID.clear();
|
||||
|
||||
const auto *keyMapper = QWindowsContext::instance()->keyMapper();
|
||||
QWindowSystemInterface::handleTouchEvent(window, m_touchDevice.data(), touchPoints,
|
||||
QWindowSystemInterface::handleTouchEvent(window, msg.time, m_touchDevice.data(), touchPoints,
|
||||
keyMapper->queryKeyboardModifiers());
|
||||
return false; // Allow mouse messages to be generated.
|
||||
}
|
||||
@ -672,7 +679,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
|
||||
|
||||
switch (msg.message) {
|
||||
case WM_POINTERENTER: {
|
||||
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, device.data(), true);
|
||||
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), true);
|
||||
m_windowUnderPointer = window;
|
||||
// The local coordinates may fall outside the window.
|
||||
// Wait until the next update to send the enter event.
|
||||
@ -685,7 +692,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
|
||||
m_windowUnderPointer = nullptr;
|
||||
m_currentWindow = nullptr;
|
||||
}
|
||||
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, device.data(), false);
|
||||
QWindowSystemInterface::handleTabletEnterLeaveProximityEvent(window, msg.time, device.data(), false);
|
||||
break;
|
||||
case WM_POINTERDOWN:
|
||||
case WM_POINTERUP:
|
||||
@ -711,7 +718,7 @@ bool QWindowsPointerHandler::translatePenEvent(QWindow *window, HWND hwnd, QtWin
|
||||
const auto *keyMapper = QWindowsContext::instance()->keyMapper();
|
||||
const Qt::KeyboardModifiers keyModifiers = keyMapper->queryKeyboardModifiers();
|
||||
|
||||
QWindowSystemInterface::handleTabletEvent(target, device.data(),
|
||||
QWindowSystemInterface::handleTabletEvent(target, msg.time, device.data(),
|
||||
localPos, hiResGlobalPos, mouseButtons,
|
||||
pressure, xTilt, yTilt, tangentialPressure,
|
||||
rotation, z, keyModifiers);
|
||||
@ -762,7 +769,7 @@ bool QWindowsPointerHandler::translateMouseWheelEvent(QWindow *window,
|
||||
|
||||
QPoint localPos = QWindowsGeometryHint::mapFromGlobal(receiver, globalPos);
|
||||
|
||||
QWindowSystemInterface::handleWheelEvent(receiver, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
|
||||
QWindowSystemInterface::handleWheelEvent(receiver, msg.time, localPos, globalPos, QPoint(), angleDelta, keyModifiers);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -818,7 +825,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
|
||||
}
|
||||
|
||||
Qt::MouseEventSource source = Qt::MouseEventNotSynthesized;
|
||||
const QPointingDevice *device = QWindowsMouseHandler::primaryMouse();
|
||||
const QPointingDevice *device = primaryMouse();
|
||||
|
||||
// Following the logic of the old mouse handler, only events synthesized
|
||||
// for touch screen are marked as such. On some systems, using the bit 7 of
|
||||
@ -861,14 +868,14 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
|
||||
&& (m_lastEventButton & mouseButtons) == 0) {
|
||||
auto releaseType = mouseEvent.type == QEvent::NonClientAreaMouseMove ?
|
||||
QEvent::NonClientAreaMouseButtonRelease : QEvent::MouseButtonRelease;
|
||||
QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons, m_lastEventButton,
|
||||
QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons, m_lastEventButton,
|
||||
releaseType, keyModifiers, source);
|
||||
}
|
||||
m_lastEventType = mouseEvent.type;
|
||||
m_lastEventButton = mouseEvent.button;
|
||||
|
||||
if (mouseEvent.type >= QEvent::NonClientAreaMouseMove && mouseEvent.type <= QEvent::NonClientAreaMouseButtonDblClick) {
|
||||
QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons,
|
||||
QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons,
|
||||
mouseEvent.button, mouseEvent.type, keyModifiers, source);
|
||||
return false; // Allow further event processing
|
||||
}
|
||||
@ -888,7 +895,7 @@ bool QWindowsPointerHandler::translateMouseEvent(QWindow *window,
|
||||
handleEnterLeave(window, currentWindowUnderPointer, globalPos);
|
||||
|
||||
if (!discardEvent && mouseEvent.type != QEvent::None) {
|
||||
QWindowSystemInterface::handleMouseEvent(window, device, localPos, globalPos, mouseButtons,
|
||||
QWindowSystemInterface::handleMouseEvent(window, msg.time, device, localPos, globalPos, mouseButtons,
|
||||
mouseEvent.button, mouseEvent.type, keyModifiers, source);
|
||||
}
|
||||
|
||||
|
@ -13,19 +13,20 @@
|
||||
# include "qwindowssystemtrayicon.h"
|
||||
#endif
|
||||
#include "qwindowsscreen.h"
|
||||
#include "qwindowswindow.h"
|
||||
#include <commctrl.h>
|
||||
#include <objbase.h>
|
||||
#ifndef Q_CC_MINGW
|
||||
# include <commoncontrols.h>
|
||||
#endif
|
||||
#include <commoncontrols.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
#include <QtCore/qapplicationstatic.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qsysinfo.h>
|
||||
#include <QtCore/qcache.h>
|
||||
#include <QtCore/qthread.h>
|
||||
#include <QtCore/qqueue.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qwaitcondition.h>
|
||||
#include <QtCore/qoperatingsystemversion.h>
|
||||
@ -52,10 +53,6 @@
|
||||
# include <winrt/Windows.UI.ViewManagement.h>
|
||||
#endif // QT_CONFIG(cpp_winrt)
|
||||
|
||||
#if defined(__IImageList_INTERFACE_DEFINED__) && defined(__IID_DEFINED__)
|
||||
# define USE_IIMAGELIST
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
@ -85,7 +82,11 @@ static inline QColor mixColors(const QColor &c1, const QColor &c2)
|
||||
|
||||
enum AccentColorLevel {
|
||||
AccentColorDarkest,
|
||||
AccentColorDarker,
|
||||
AccentColorDark,
|
||||
AccentColorNormal,
|
||||
AccentColorLight,
|
||||
AccentColorLighter,
|
||||
AccentColorLightest
|
||||
};
|
||||
|
||||
@ -100,6 +101,10 @@ static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color)
|
||||
{
|
||||
QColor accent;
|
||||
QColor accentLight;
|
||||
QColor accentLighter;
|
||||
QColor accentLightest;
|
||||
QColor accentDark;
|
||||
QColor accentDarker;
|
||||
QColor accentDarkest;
|
||||
|
||||
#if QT_CONFIG(cpp_winrt)
|
||||
@ -109,6 +114,10 @@ static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color)
|
||||
const auto settings = UISettings();
|
||||
accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
|
||||
accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
|
||||
accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
|
||||
accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
|
||||
accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
|
||||
accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
|
||||
accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
|
||||
}
|
||||
#endif
|
||||
@ -128,14 +137,30 @@ static constexpr QColor getSysColor(winrt::Windows::UI::Color &&color)
|
||||
return {};
|
||||
accent = QColor::fromRgb(abgr.blue(), abgr.green(), abgr.red(), abgr.alpha());
|
||||
accentLight = accent.lighter(120);
|
||||
accentLighter = accentLight.lighter(120);
|
||||
accentLightest = accentLighter.lighter(120);
|
||||
accentDarkest = accent.darker(120 * 120 * 120);
|
||||
accentDark = accent.darker(120);
|
||||
accentDarker = accentDark.darker(120);
|
||||
accentDarkest = accentDarker.darker(120);
|
||||
}
|
||||
|
||||
if (level == AccentColorDarkest)
|
||||
switch (level) {
|
||||
case AccentColorDarkest:
|
||||
return accentDarkest;
|
||||
else if (level == AccentColorLightest)
|
||||
case AccentColorDarker:
|
||||
return accentDarker;
|
||||
case AccentColorDark:
|
||||
return accentDark;
|
||||
case AccentColorLight:
|
||||
return accentLight;
|
||||
return accent;
|
||||
case AccentColorLighter:
|
||||
return accentLighter;
|
||||
case AccentColorLightest:
|
||||
return accentLightest;
|
||||
default:
|
||||
return accent;
|
||||
}
|
||||
}
|
||||
|
||||
static inline QColor getSysColor(int index)
|
||||
@ -148,107 +173,106 @@ static inline QColor getSysColor(int index)
|
||||
// models has been observed to trigger a WM_PAINT on the mainwindow. Suppress the
|
||||
// behavior by running it in a thread.
|
||||
|
||||
struct QShGetFileInfoParams
|
||||
{
|
||||
QShGetFileInfoParams(const QString &fn, DWORD a, SHFILEINFO *i, UINT f, bool *r)
|
||||
: fileName(fn), attributes(a), flags(f), info(i), result(r)
|
||||
{ }
|
||||
|
||||
const QString &fileName;
|
||||
const DWORD attributes;
|
||||
const UINT flags;
|
||||
SHFILEINFO *const info;
|
||||
bool *const result;
|
||||
};
|
||||
|
||||
class QShGetFileInfoThread : public QThread
|
||||
{
|
||||
public:
|
||||
explicit QShGetFileInfoThread()
|
||||
: QThread(), m_params(nullptr)
|
||||
struct Task
|
||||
{
|
||||
connect(this, &QThread::finished, this, &QObject::deleteLater);
|
||||
Task(const QString &fn, DWORD a, UINT f)
|
||||
: fileName(fn), attributes(a), flags(f)
|
||||
{}
|
||||
Q_DISABLE_COPY(Task)
|
||||
~Task()
|
||||
{
|
||||
DestroyIcon(hIcon);
|
||||
hIcon = 0;
|
||||
}
|
||||
// Request
|
||||
const QString fileName;
|
||||
const DWORD attributes;
|
||||
const UINT flags;
|
||||
// Result
|
||||
HICON hIcon = 0;
|
||||
int iIcon = -1;
|
||||
bool finished = false;
|
||||
bool resultValid() const { return hIcon != 0 && iIcon >= 0 && finished; }
|
||||
};
|
||||
|
||||
QShGetFileInfoThread()
|
||||
: QThread()
|
||||
{
|
||||
start();
|
||||
}
|
||||
|
||||
~QShGetFileInfoThread()
|
||||
{
|
||||
cancel();
|
||||
wait();
|
||||
}
|
||||
|
||||
QSharedPointer<Task> getNextTask()
|
||||
{
|
||||
QMutexLocker l(&m_waitForTaskMutex);
|
||||
while (!isInterruptionRequested()) {
|
||||
if (!m_taskQueue.isEmpty())
|
||||
return m_taskQueue.dequeue();
|
||||
m_waitForTaskCondition.wait(&m_waitForTaskMutex);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
QComHelper comHelper(COINIT_MULTITHREADED);
|
||||
|
||||
QMutexLocker readyLocker(&m_readyMutex);
|
||||
while (!m_cancelled.loadRelaxed()) {
|
||||
if (!m_params && !m_cancelled.loadRelaxed()
|
||||
&& !m_readyCondition.wait(&m_readyMutex, QDeadlineTimer(1000ll)))
|
||||
continue;
|
||||
|
||||
if (m_params) {
|
||||
const QString fileName = m_params->fileName;
|
||||
while (!isInterruptionRequested()) {
|
||||
auto task = getNextTask();
|
||||
if (task) {
|
||||
SHFILEINFO info;
|
||||
const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(fileName.utf16()),
|
||||
m_params->attributes, &info, sizeof(SHFILEINFO),
|
||||
m_params->flags);
|
||||
m_doneMutex.lock();
|
||||
if (!m_cancelled.loadRelaxed()) {
|
||||
*m_params->result = result;
|
||||
memcpy(m_params->info, &info, sizeof(SHFILEINFO));
|
||||
const bool result = SHGetFileInfo(reinterpret_cast<const wchar_t *>(task->fileName.utf16()),
|
||||
task->attributes, &info, sizeof(SHFILEINFO),
|
||||
task->flags);
|
||||
if (result) {
|
||||
task->hIcon = info.hIcon;
|
||||
task->iIcon = info.iIcon;
|
||||
}
|
||||
m_params = nullptr;
|
||||
|
||||
task->finished = true;
|
||||
m_doneCondition.wakeAll();
|
||||
m_doneMutex.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs)
|
||||
void runWithParams(const QSharedPointer<Task> &task,
|
||||
std::chrono::milliseconds timeout = std::chrono::milliseconds(5000))
|
||||
{
|
||||
{
|
||||
QMutexLocker l(&m_waitForTaskMutex);
|
||||
m_taskQueue.enqueue(task);
|
||||
m_waitForTaskCondition.wakeAll();
|
||||
}
|
||||
|
||||
QMutexLocker doneLocker(&m_doneMutex);
|
||||
|
||||
m_readyMutex.lock();
|
||||
m_params = params;
|
||||
m_readyCondition.wakeAll();
|
||||
m_readyMutex.unlock();
|
||||
|
||||
return m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeOutMSecs));
|
||||
while (!task->finished && !isInterruptionRequested()) {
|
||||
if (!m_doneCondition.wait(&m_doneMutex, QDeadlineTimer(timeout)))
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void cancel()
|
||||
{
|
||||
QMutexLocker doneLocker(&m_doneMutex);
|
||||
m_cancelled.storeRelaxed(1);
|
||||
m_readyCondition.wakeAll();
|
||||
requestInterruption();
|
||||
m_doneCondition.wakeAll();
|
||||
m_waitForTaskCondition.wakeAll();
|
||||
}
|
||||
|
||||
private:
|
||||
QShGetFileInfoParams *m_params;
|
||||
QAtomicInt m_cancelled;
|
||||
QWaitCondition m_readyCondition;
|
||||
QQueue<QSharedPointer<Task>> m_taskQueue;
|
||||
QWaitCondition m_doneCondition;
|
||||
QMutex m_readyMutex;
|
||||
QWaitCondition m_waitForTaskCondition;
|
||||
QMutex m_doneMutex;
|
||||
QMutex m_waitForTaskMutex;
|
||||
};
|
||||
|
||||
static bool shGetFileInfoBackground(const QString &fileName, DWORD attributes,
|
||||
SHFILEINFO *info, UINT flags,
|
||||
qint64 timeOutMSecs = 5000)
|
||||
{
|
||||
static QShGetFileInfoThread *getFileInfoThread = nullptr;
|
||||
if (!getFileInfoThread) {
|
||||
getFileInfoThread = new QShGetFileInfoThread;
|
||||
getFileInfoThread->start();
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
QShGetFileInfoParams params(fileName, attributes, info, flags, &result);
|
||||
if (!getFileInfoThread->runWithParams(¶ms, timeOutMSecs)) {
|
||||
// Cancel and reset getFileInfoThread. It'll
|
||||
// be reinitialized the next time we get called.
|
||||
getFileInfoThread->cancel();
|
||||
getFileInfoThread = nullptr;
|
||||
qWarning().noquote() << "SHGetFileInfo() timed out for " << fileName;
|
||||
return false;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
Q_APPLICATION_STATIC(QShGetFileInfoThread, s_shGetFileInfoThread)
|
||||
|
||||
// from QStyle::standardPalette
|
||||
static inline QPalette standardPalette()
|
||||
@ -277,15 +301,16 @@ static QColor placeHolderColor(QColor textColor)
|
||||
This is used when the theme is light mode, and when the theme is dark but the
|
||||
application doesn't support dark mode. In the latter case, we need to check.
|
||||
*/
|
||||
static void populateLightSystemBasePalette(QPalette &result)
|
||||
void QWindowsTheme::populateLightSystemBasePalette(QPalette &result)
|
||||
{
|
||||
const QColor background = getSysColor(COLOR_BTNFACE);
|
||||
const QColor textColor = getSysColor(COLOR_WINDOWTEXT);
|
||||
|
||||
const QColor accent = qt_accentColor(AccentColorNormal);
|
||||
const QColor accentDark = qt_accentColor(AccentColorDark);
|
||||
const QColor accentDarker = qt_accentColor(AccentColorDarker);
|
||||
const QColor accentDarkest = qt_accentColor(AccentColorDarkest);
|
||||
|
||||
const QColor linkColor = accent;
|
||||
const QColor linkColor = accentDarker;
|
||||
const QColor btnFace = background;
|
||||
const QColor btnHighlight = getSysColor(COLOR_BTNHIGHLIGHT);
|
||||
|
||||
@ -304,7 +329,7 @@ static void populateLightSystemBasePalette(QPalette &result)
|
||||
result.setColor(QPalette::Midlight, getSysColor(COLOR_3DLIGHT));
|
||||
result.setColor(QPalette::Shadow, getSysColor(COLOR_3DDKSHADOW));
|
||||
result.setColor(QPalette::HighlightedText, getSysColor(COLOR_HIGHLIGHTTEXT));
|
||||
result.setColor(QPalette::Accent, accent);
|
||||
result.setColor(QPalette::Accent, accentDark); // default accent color for controls on Light mode is AccentDark1
|
||||
|
||||
result.setColor(QPalette::Link, linkColor);
|
||||
result.setColor(QPalette::LinkVisited, accentDarkest);
|
||||
@ -317,7 +342,7 @@ static void populateLightSystemBasePalette(QPalette &result)
|
||||
result.setColor(QPalette::Midlight, result.button().color().lighter(110));
|
||||
}
|
||||
|
||||
static void populateDarkSystemBasePalette(QPalette &result)
|
||||
void QWindowsTheme::populateDarkSystemBasePalette(QPalette &result)
|
||||
{
|
||||
QColor foreground = Qt::white;
|
||||
QColor background = QColor(0x1E, 0x1E, 0x1E);
|
||||
@ -338,25 +363,27 @@ static void populateDarkSystemBasePalette(QPalette &result)
|
||||
// We have to craft a palette from these colors. The settings.UIElementColor(UIElementType) API
|
||||
// returns the old system colors, not the dark mode colors. If the background is black (which it
|
||||
// usually), then override it with a dark gray instead so that we can go up and down the lightness.
|
||||
foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
|
||||
background = [&settings]() -> QColor {
|
||||
auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
|
||||
if (systemBackground == Qt::black)
|
||||
systemBackground = QColor(0x1E, 0x1E, 0x1E);
|
||||
return systemBackground;
|
||||
}();
|
||||
|
||||
accent = getSysColor(settings.GetColorValue(UIColorType::Accent));
|
||||
accentDark = getSysColor(settings.GetColorValue(UIColorType::AccentDark1));
|
||||
accentDarker = getSysColor(settings.GetColorValue(UIColorType::AccentDark2));
|
||||
accentDarkest = getSysColor(settings.GetColorValue(UIColorType::AccentDark3));
|
||||
accentLight = getSysColor(settings.GetColorValue(UIColorType::AccentLight1));
|
||||
accentLighter = getSysColor(settings.GetColorValue(UIColorType::AccentLight2));
|
||||
accentLightest = getSysColor(settings.GetColorValue(UIColorType::AccentLight3));
|
||||
if (QWindowsTheme::queryColorScheme() == Qt::ColorScheme::Dark) {
|
||||
// the system is actually running in dark mode, so UISettings will give us dark colors
|
||||
foreground = getSysColor(settings.GetColorValue(UIColorType::Foreground));
|
||||
background = [&settings]() -> QColor {
|
||||
auto systemBackground = getSysColor(settings.GetColorValue(UIColorType::Background));
|
||||
if (systemBackground == Qt::black)
|
||||
systemBackground = QColor(0x1E, 0x1E, 0x1E);
|
||||
return systemBackground;
|
||||
}();
|
||||
accent = qt_accentColor(AccentColorNormal);
|
||||
accentDark = qt_accentColor(AccentColorDark);
|
||||
accentDarker = qt_accentColor(AccentColorDarker);
|
||||
accentDarkest = qt_accentColor(AccentColorDarkest);
|
||||
accentLight = qt_accentColor(AccentColorLight);
|
||||
accentLighter = qt_accentColor(AccentColorLighter);
|
||||
accentLightest = qt_accentColor(AccentColorLightest);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
const QColor linkColor = accent;
|
||||
const QColor linkColor = accentLightest;
|
||||
const QColor buttonColor = background.lighter(200);
|
||||
|
||||
result.setColor(QPalette::All, QPalette::WindowText, foreground);
|
||||
@ -377,12 +404,12 @@ static void populateDarkSystemBasePalette(QPalette &result)
|
||||
result.setColor(QPalette::All, QPalette::Highlight, accent);
|
||||
result.setColor(QPalette::All, QPalette::HighlightedText, accent.lightness() > 128 ? Qt::black : Qt::white);
|
||||
result.setColor(QPalette::All, QPalette::Link, linkColor);
|
||||
result.setColor(QPalette::All, QPalette::LinkVisited, accentDarkest);
|
||||
result.setColor(QPalette::All, QPalette::LinkVisited, accentLighter);
|
||||
result.setColor(QPalette::All, QPalette::AlternateBase, accentDarkest);
|
||||
result.setColor(QPalette::All, QPalette::ToolTipBase, buttonColor);
|
||||
result.setColor(QPalette::All, QPalette::ToolTipText, foreground.darker(120));
|
||||
result.setColor(QPalette::All, QPalette::PlaceholderText, placeHolderColor(foreground));
|
||||
result.setColor(QPalette::All, QPalette::Accent, accent);
|
||||
result.setColor(QPalette::All, QPalette::Accent, accentLighter);
|
||||
}
|
||||
|
||||
static inline QPalette toolTipPalette(const QPalette &systemPalette, bool light)
|
||||
@ -474,6 +501,7 @@ QWindowsTheme *QWindowsTheme::m_instance = nullptr;
|
||||
QWindowsTheme::QWindowsTheme()
|
||||
{
|
||||
m_instance = this;
|
||||
s_colorScheme = QWindowsTheme::queryColorScheme();
|
||||
std::fill(m_fonts, m_fonts + NFonts, nullptr);
|
||||
std::fill(m_palettes, m_palettes + NPalettes, nullptr);
|
||||
refresh();
|
||||
@ -562,10 +590,44 @@ QVariant QWindowsTheme::themeHint(ThemeHint hint) const
|
||||
}
|
||||
|
||||
Qt::ColorScheme QWindowsTheme::colorScheme() const
|
||||
{
|
||||
return QWindowsTheme::effectiveColorScheme();
|
||||
}
|
||||
|
||||
Qt::ColorScheme QWindowsTheme::effectiveColorScheme()
|
||||
{
|
||||
if (queryHighContrast())
|
||||
return Qt::ColorScheme::Unknown;
|
||||
return QWindowsContext::isDarkMode() ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
|
||||
if (s_colorSchemeOverride != Qt::ColorScheme::Unknown)
|
||||
return s_colorSchemeOverride;
|
||||
if (s_colorScheme != Qt::ColorScheme::Unknown)
|
||||
return s_colorScheme;
|
||||
return queryColorScheme();
|
||||
}
|
||||
|
||||
void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme)
|
||||
{
|
||||
s_colorSchemeOverride = scheme;
|
||||
handleSettingsChanged();
|
||||
}
|
||||
|
||||
void QWindowsTheme::handleSettingsChanged()
|
||||
{
|
||||
const auto oldColorScheme = s_colorScheme;
|
||||
s_colorScheme = Qt::ColorScheme::Unknown; // make effectiveColorScheme() query registry
|
||||
const auto newColorScheme = effectiveColorScheme();
|
||||
const bool colorSchemeChanged = newColorScheme != oldColorScheme;
|
||||
s_colorScheme = newColorScheme;
|
||||
auto integration = QWindowsIntegration::instance();
|
||||
integration->updateApplicationBadge();
|
||||
if (integration->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle)) {
|
||||
QWindowsTheme::instance()->refresh();
|
||||
QWindowSystemInterface::handleThemeChange<QWindowSystemInterface::SynchronousDelivery>();
|
||||
}
|
||||
if (colorSchemeChanged) {
|
||||
for (QWindowsWindow *w : std::as_const(QWindowsContext::instance()->windows()))
|
||||
w->setDarkBorder(s_colorScheme == Qt::ColorScheme::Dark);
|
||||
}
|
||||
}
|
||||
|
||||
void QWindowsTheme::clearPalettes()
|
||||
@ -579,17 +641,17 @@ void QWindowsTheme::refreshPalettes()
|
||||
if (!QGuiApplication::desktopSettingsAware())
|
||||
return;
|
||||
const bool light =
|
||||
!QWindowsContext::isDarkMode()
|
||||
effectiveColorScheme() != Qt::ColorScheme::Dark
|
||||
|| !QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeStyle);
|
||||
clearPalettes();
|
||||
m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(light ? Qt::ColorScheme::Light : Qt::ColorScheme::Dark));
|
||||
m_palettes[SystemPalette] = new QPalette(QWindowsTheme::systemPalette(s_colorScheme));
|
||||
m_palettes[ToolTipPalette] = new QPalette(toolTipPalette(*m_palettes[SystemPalette], light));
|
||||
m_palettes[MenuPalette] = new QPalette(menuPalette(*m_palettes[SystemPalette], light));
|
||||
m_palettes[MenuBarPalette] = menuBarPalette(*m_palettes[MenuPalette], light);
|
||||
if (!light) {
|
||||
m_palettes[CheckBoxPalette] = new QPalette(*m_palettes[SystemPalette]);
|
||||
m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Base, qt_accentColor(AccentColorNormal));
|
||||
m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, qt_accentColor(AccentColorLightest));
|
||||
m_palettes[CheckBoxPalette]->setColor(QPalette::Active, QPalette::Button, qt_accentColor(AccentColorLighter));
|
||||
m_palettes[CheckBoxPalette]->setColor(QPalette::Inactive, QPalette::Base, qt_accentColor(AccentColorDarkest));
|
||||
m_palettes[RadioButtonPalette] = new QPalette(*m_palettes[CheckBoxPalette]);
|
||||
}
|
||||
@ -600,15 +662,15 @@ QPalette QWindowsTheme::systemPalette(Qt::ColorScheme colorScheme)
|
||||
QPalette result = standardPalette();
|
||||
|
||||
switch (colorScheme) {
|
||||
case Qt::ColorScheme::Unknown:
|
||||
// when a high-contrast theme is active or when we fail to read, assume light
|
||||
Q_FALLTHROUGH();
|
||||
case Qt::ColorScheme::Light:
|
||||
populateLightSystemBasePalette(result);
|
||||
break;
|
||||
case Qt::ColorScheme::Dark:
|
||||
populateDarkSystemBasePalette(result);
|
||||
break;
|
||||
default:
|
||||
qFatal("Unknown color scheme");
|
||||
break;
|
||||
}
|
||||
|
||||
if (result.window() != result.base()) {
|
||||
@ -750,11 +812,7 @@ void QWindowsTheme::refreshIconPixmapSizes()
|
||||
fileIconSizes[LargeFileIcon] + fileIconSizes[LargeFileIcon] / 2;
|
||||
fileIconSizes[JumboFileIcon] = 8 * fileIconSizes[LargeFileIcon]; // empirical, has not been observed to work
|
||||
|
||||
#ifdef USE_IIMAGELIST
|
||||
int *availEnd = fileIconSizes + JumboFileIcon + 1;
|
||||
#else
|
||||
int *availEnd = fileIconSizes + LargeFileIcon + 1;
|
||||
#endif // USE_IIMAGELIST
|
||||
m_fileIconSizes = QAbstractFileIconEngine::toSizeList(fileIconSizes, availEnd);
|
||||
qCDebug(lcQpaWindow) << __FUNCTION__ << m_fileIconSizes;
|
||||
}
|
||||
@ -948,10 +1006,10 @@ public:
|
||||
|
||||
// Shell image list helper functions.
|
||||
|
||||
static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
|
||||
static QPixmap pixmapFromShellImageList(int iImageList, int iIcon)
|
||||
{
|
||||
QPixmap result;
|
||||
#ifdef USE_IIMAGELIST
|
||||
|
||||
// For MinGW:
|
||||
static const IID iID_IImageList = {0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50}};
|
||||
|
||||
@ -960,16 +1018,13 @@ static QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info)
|
||||
if (hr != S_OK)
|
||||
return result;
|
||||
HICON hIcon;
|
||||
hr = imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon);
|
||||
hr = imageList->GetIcon(iIcon, ILD_TRANSPARENT, &hIcon);
|
||||
if (hr == S_OK) {
|
||||
result = qt_pixmapFromWinHICON(hIcon);
|
||||
DestroyIcon(hIcon);
|
||||
}
|
||||
imageList->Release();
|
||||
#else
|
||||
Q_UNUSED(iImageList);
|
||||
Q_UNUSED(info);
|
||||
#endif // USE_IIMAGELIST
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -1021,13 +1076,9 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
|
||||
const int width = int(size.width());
|
||||
const int iconSize = width > fileIconSizes[SmallFileIcon] ? SHGFI_LARGEICON : SHGFI_SMALLICON;
|
||||
const int requestedImageListSize =
|
||||
#ifdef USE_IIMAGELIST
|
||||
width > fileIconSizes[ExtraLargeFileIcon]
|
||||
? sHIL_JUMBO
|
||||
: (width > fileIconSizes[LargeFileIcon] ? sHIL_EXTRALARGE : 0);
|
||||
#else
|
||||
0;
|
||||
#endif // !USE_IIMAGELIST
|
||||
bool cacheableDirIcon = fileInfo().isDir() && !fileInfo().isRoot();
|
||||
if (cacheableDirIcon) {
|
||||
QMutexLocker locker(&mx);
|
||||
@ -1043,7 +1094,6 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
|
||||
}
|
||||
}
|
||||
|
||||
SHFILEINFO info;
|
||||
unsigned int flags = SHGFI_ICON | iconSize | SHGFI_SYSICONINDEX | SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX;
|
||||
DWORD attributes = 0;
|
||||
QString path = filePath;
|
||||
@ -1055,43 +1105,43 @@ QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon
|
||||
flags |= SHGFI_USEFILEATTRIBUTES;
|
||||
attributes |= FILE_ATTRIBUTE_NORMAL;
|
||||
}
|
||||
const bool val = shGetFileInfoBackground(path, attributes, &info, flags);
|
||||
|
||||
auto task = QSharedPointer<QShGetFileInfoThread::Task>(
|
||||
new QShGetFileInfoThread::Task(path, attributes, flags));
|
||||
s_shGetFileInfoThread()->runWithParams(task);
|
||||
// Even if GetFileInfo returns a valid result, hIcon can be empty in some cases
|
||||
if (val && info.hIcon) {
|
||||
if (task->resultValid()) {
|
||||
QString key;
|
||||
if (cacheableDirIcon) {
|
||||
if (useDefaultFolderIcon && defaultFolderIIcon < 0)
|
||||
defaultFolderIIcon = info.iIcon;
|
||||
defaultFolderIIcon = task->iIcon;
|
||||
|
||||
//using the unique icon index provided by windows save us from duplicate keys
|
||||
key = dirIconPixmapCacheKey(info.iIcon, iconSize, requestedImageListSize);
|
||||
key = dirIconPixmapCacheKey(task->iIcon, iconSize, requestedImageListSize);
|
||||
QPixmapCache::find(key, &pixmap);
|
||||
if (!pixmap.isNull()) {
|
||||
QMutexLocker locker(&mx);
|
||||
dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
|
||||
dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon));
|
||||
}
|
||||
}
|
||||
|
||||
if (pixmap.isNull()) {
|
||||
if (requestedImageListSize) {
|
||||
pixmap = pixmapFromShellImageList(requestedImageListSize, info);
|
||||
pixmap = pixmapFromShellImageList(requestedImageListSize, task->iIcon);
|
||||
if (pixmap.isNull() && requestedImageListSize == sHIL_JUMBO)
|
||||
pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, info);
|
||||
pixmap = pixmapFromShellImageList(sHIL_EXTRALARGE, task->iIcon);
|
||||
}
|
||||
if (pixmap.isNull())
|
||||
pixmap = qt_pixmapFromWinHICON(info.hIcon);
|
||||
pixmap = qt_pixmapFromWinHICON(task->hIcon);
|
||||
if (!pixmap.isNull()) {
|
||||
if (cacheableDirIcon) {
|
||||
QMutexLocker locker(&mx);
|
||||
QPixmapCache::insert(key, pixmap);
|
||||
dirIconEntryCache.insert(filePath, FakePointer<int>::create(info.iIcon));
|
||||
dirIconEntryCache.insert(filePath, FakePointer<int>::create(task->iIcon));
|
||||
}
|
||||
} else {
|
||||
qWarning("QWindowsTheme::fileIconPixmap() no icon found");
|
||||
}
|
||||
}
|
||||
DestroyIcon(info.hIcon);
|
||||
}
|
||||
|
||||
return pixmap;
|
||||
@ -1131,14 +1181,14 @@ bool QWindowsTheme::useNativeMenus()
|
||||
return result;
|
||||
}
|
||||
|
||||
bool QWindowsTheme::queryDarkMode()
|
||||
Qt::ColorScheme QWindowsTheme::queryColorScheme()
|
||||
{
|
||||
if (queryHighContrast()) {
|
||||
return false;
|
||||
}
|
||||
if (queryHighContrast())
|
||||
return Qt::ColorScheme::Unknown;
|
||||
|
||||
const auto setting = QWinRegistryKey(HKEY_CURRENT_USER, LR"(Software\Microsoft\Windows\CurrentVersion\Themes\Personalize)")
|
||||
.dwordValue(L"AppsUseLightTheme");
|
||||
return setting.second && setting.first == 0;
|
||||
return setting.second && setting.first == 0 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light;
|
||||
}
|
||||
|
||||
bool QWindowsTheme::queryHighContrast()
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
#include "qwindowswindow.h"
|
||||
#include "qwindowscontext.h"
|
||||
#include "qwindowstheme.h"
|
||||
#if QT_CONFIG(draganddrop)
|
||||
# include "qwindowsdrag.h"
|
||||
#endif
|
||||
@ -867,7 +868,8 @@ static inline bool shouldApplyDarkFrame(const QWindow *w)
|
||||
{
|
||||
if (!w->isTopLevel() || w->flags().testFlag(Qt::FramelessWindowHint))
|
||||
return false;
|
||||
// the application has explicitly opted out of dark frames
|
||||
|
||||
// the user of the application has explicitly opted out of dark frames
|
||||
if (!QWindowsIntegration::instance()->darkModeHandling().testFlag(QWindowsApplication::DarkModeWindowFrames))
|
||||
return false;
|
||||
|
||||
@ -947,7 +949,7 @@ QWindowsWindowData
|
||||
return result;
|
||||
}
|
||||
|
||||
if (QWindowsContext::isDarkMode() && shouldApplyDarkFrame(w))
|
||||
if (QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark && shouldApplyDarkFrame(w))
|
||||
QWindowsWindow::setDarkBorderToWindow(result.hwnd, true);
|
||||
|
||||
if (mirrorParentWidth != 0) {
|
||||
@ -1387,6 +1389,12 @@ QWindowsForeignWindow::QWindowsForeignWindow(QWindow *window, HWND hwnd)
|
||||
setParent(QPlatformWindow::parent());
|
||||
}
|
||||
|
||||
QWindowsForeignWindow::~QWindowsForeignWindow()
|
||||
{
|
||||
if (QPlatformWindow::parent())
|
||||
setParent(nullptr);
|
||||
}
|
||||
|
||||
void QWindowsForeignWindow::setParent(const QPlatformWindow *newParentWindow)
|
||||
{
|
||||
const bool wasTopLevel = isTopLevel_sys();
|
||||
@ -2739,7 +2747,7 @@ bool QWindowsWindow::windowEvent(QEvent *event)
|
||||
{
|
||||
switch (event->type()) {
|
||||
case QEvent::ApplicationPaletteChange:
|
||||
setDarkBorder(QWindowsContext::isDarkMode());
|
||||
setDarkBorder(QWindowsTheme::instance()->colorScheme() == Qt::ColorScheme::Dark);
|
||||
break;
|
||||
case QEvent::WindowBlocked: // Blocked by another modal window.
|
||||
setEnabled(false);
|
||||
@ -3335,17 +3343,6 @@ enum : WORD {
|
||||
DwmwaUseImmersiveDarkModeBefore20h1 = 19
|
||||
};
|
||||
|
||||
static bool queryDarkBorder(HWND hwnd)
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
const bool ok =
|
||||
SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkMode, &result, sizeof(result)))
|
||||
|| SUCCEEDED(DwmGetWindowAttribute(hwnd, DwmwaUseImmersiveDarkModeBefore20h1, &result, sizeof(result)));
|
||||
if (!ok)
|
||||
qCWarning(lcQpaWindow, "%s: Unable to retrieve dark window border setting.", __FUNCTION__);
|
||||
return result == TRUE;
|
||||
}
|
||||
|
||||
bool QWindowsWindow::setDarkBorderToWindow(HWND hwnd, bool d)
|
||||
{
|
||||
const BOOL darkBorder = d ? TRUE : FALSE;
|
||||
@ -3361,8 +3358,6 @@ void QWindowsWindow::setDarkBorder(bool d)
|
||||
{
|
||||
// respect explicit opt-out and incompatible palettes or styles
|
||||
d = d && shouldApplyDarkFrame(window());
|
||||
if (queryDarkBorder(m_data.hwnd) == d)
|
||||
return;
|
||||
|
||||
setDarkBorderToWindow(m_data.hwnd, d);
|
||||
}
|
||||
|
@ -0,0 +1,170 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <QtGui/qtguiglobal.h>
|
||||
#if QT_CONFIG(accessibility)
|
||||
|
||||
#include "qwindowsuiaaccessibility.h"
|
||||
#include "qwindowsuiautomation.h"
|
||||
#include "qwindowsuiamainprovider.h"
|
||||
#include "qwindowsuiautils.h"
|
||||
|
||||
#include <QtGui/qaccessible.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/private/qguiapplication_p.h>
|
||||
#include <QtCore/qt_windows.h>
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#include "qwindowsuiawrapper_p.h"
|
||||
#include "qwindowsuiawrapper.cpp"
|
||||
|
||||
#include <QtCore/private/qwinregistry_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace QWindowsUiAutomation;
|
||||
using namespace Qt::Literals::StringLiterals;
|
||||
|
||||
bool QWindowsUiaAccessibility::m_accessibleActive = false;
|
||||
|
||||
QWindowsUiaAccessibility::QWindowsUiaAccessibility()
|
||||
{
|
||||
}
|
||||
|
||||
QWindowsUiaAccessibility::~QWindowsUiaAccessibility()
|
||||
{
|
||||
}
|
||||
|
||||
// Handles UI Automation window messages.
|
||||
bool QWindowsUiaAccessibility::handleWmGetObject(HWND hwnd, WPARAM wParam, LPARAM lParam, LRESULT *lResult)
|
||||
{
|
||||
// Start handling accessibility internally
|
||||
QGuiApplicationPrivate::platformIntegration()->accessibility()->setActive(true);
|
||||
m_accessibleActive = true;
|
||||
|
||||
// Ignoring all requests while starting up / shutting down
|
||||
if (QCoreApplication::startingUp() || QCoreApplication::closingDown())
|
||||
return false;
|
||||
|
||||
if (QWindow *window = QWindowsContext::instance()->findWindow(hwnd)) {
|
||||
if (QAccessibleInterface *accessible = window->accessibleRoot()) {
|
||||
auto provider = QWindowsUiaMainProvider::providerForAccessible(accessible);
|
||||
*lResult = QWindowsUiaWrapper::instance()->returnRawElementProvider(hwnd, wParam, lParam, provider.Get());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Retrieve sound name by checking the icon property of a message box
|
||||
// should it be the event object.
|
||||
static QString alertSound(const QObject *object)
|
||||
{
|
||||
if (object->inherits("QMessageBox")) {
|
||||
enum MessageBoxIcon { // Keep in sync with QMessageBox::Icon
|
||||
Information = 1,
|
||||
Warning = 2,
|
||||
Critical = 3
|
||||
};
|
||||
switch (object->property("icon").toInt()) {
|
||||
case Information:
|
||||
return QStringLiteral("SystemAsterisk");
|
||||
case Warning:
|
||||
return QStringLiteral("SystemExclamation");
|
||||
case Critical:
|
||||
return QStringLiteral("SystemHand");
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
return QStringLiteral("SystemAsterisk");
|
||||
}
|
||||
|
||||
static QString soundFileName(const QString &soundName)
|
||||
{
|
||||
const QString key = "AppEvents\\Schemes\\Apps\\.Default\\"_L1
|
||||
+ soundName + "\\.Current"_L1;
|
||||
return QWinRegistryKey(HKEY_CURRENT_USER, key).stringValue(L"");
|
||||
}
|
||||
|
||||
static void playSystemSound(const QString &soundName)
|
||||
{
|
||||
if (!soundName.isEmpty() && !soundFileName(soundName).isEmpty()) {
|
||||
PlaySound(reinterpret_cast<const wchar_t *>(soundName.utf16()), nullptr,
|
||||
SND_ALIAS | SND_ASYNC | SND_NODEFAULT | SND_NOWAIT);
|
||||
}
|
||||
}
|
||||
|
||||
// Handles accessibility update notifications.
|
||||
void QWindowsUiaAccessibility::notifyAccessibilityUpdate(QAccessibleEvent *event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
// Always handle system sound events
|
||||
switch (event->type()) {
|
||||
case QAccessible::PopupMenuStart:
|
||||
playSystemSound(QStringLiteral("MenuPopup"));
|
||||
break;
|
||||
case QAccessible::MenuCommand:
|
||||
playSystemSound(QStringLiteral("MenuCommand"));
|
||||
break;
|
||||
case QAccessible::Alert:
|
||||
playSystemSound(alertSound(event->object()));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore events sent before the first UI Automation
|
||||
// request or while QAccessible is being activated.
|
||||
if (!m_accessibleActive)
|
||||
return;
|
||||
|
||||
QAccessibleInterface *accessible = event->accessibleInterface();
|
||||
if (!isActive() || !accessible || !accessible->isValid())
|
||||
return;
|
||||
|
||||
// Ensures QWindowsUiaWrapper is properly initialized.
|
||||
if (!QWindowsUiaWrapper::instance()->ready())
|
||||
return;
|
||||
|
||||
// No need to do anything when nobody is listening.
|
||||
if (!QWindowsUiaWrapper::instance()->clientsAreListening())
|
||||
return;
|
||||
|
||||
switch (event->type()) {
|
||||
case QAccessible::Announcement:
|
||||
QWindowsUiaMainProvider::raiseNotification(static_cast<QAccessibleAnnouncementEvent *>(event));
|
||||
break;
|
||||
case QAccessible::Focus:
|
||||
QWindowsUiaMainProvider::notifyFocusChange(event);
|
||||
break;
|
||||
case QAccessible::StateChanged:
|
||||
QWindowsUiaMainProvider::notifyStateChange(static_cast<QAccessibleStateChangeEvent *>(event));
|
||||
break;
|
||||
case QAccessible::ValueChanged:
|
||||
QWindowsUiaMainProvider::notifyValueChange(static_cast<QAccessibleValueChangeEvent *>(event));
|
||||
break;
|
||||
case QAccessible::NameChanged:
|
||||
QWindowsUiaMainProvider::notifyNameChange(event);
|
||||
break;
|
||||
case QAccessible::SelectionAdd:
|
||||
QWindowsUiaMainProvider::notifySelectionChange(event);
|
||||
break;
|
||||
case QAccessible::TextAttributeChanged:
|
||||
case QAccessible::TextColumnChanged:
|
||||
case QAccessible::TextInserted:
|
||||
case QAccessible::TextRemoved:
|
||||
case QAccessible::TextUpdated:
|
||||
case QAccessible::TextSelectionChanged:
|
||||
case QAccessible::TextCaretMoved:
|
||||
QWindowsUiaMainProvider::notifyTextChange(event);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_CONFIG(accessibility)
|
@ -0,0 +1,843 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <QtGui/qtguiglobal.h>
|
||||
#if QT_CONFIG(accessibility)
|
||||
|
||||
#include "qwindowsuiamainprovider.h"
|
||||
#include "qwindowsuiavalueprovider.h"
|
||||
#include "qwindowsuiarangevalueprovider.h"
|
||||
#include "qwindowsuiatextprovider.h"
|
||||
#include "qwindowsuiatoggleprovider.h"
|
||||
#include "qwindowsuiainvokeprovider.h"
|
||||
#include "qwindowsuiaselectionprovider.h"
|
||||
#include "qwindowsuiaselectionitemprovider.h"
|
||||
#include "qwindowsuiatableprovider.h"
|
||||
#include "qwindowsuiatableitemprovider.h"
|
||||
#include "qwindowsuiagridprovider.h"
|
||||
#include "qwindowsuiagriditemprovider.h"
|
||||
#include "qwindowsuiawindowprovider.h"
|
||||
#include "qwindowsuiaexpandcollapseprovider.h"
|
||||
#include "qwindowscontext.h"
|
||||
#include "qwindowsuiautils.h"
|
||||
#include "qwindowsuiaprovidercache.h"
|
||||
#include "qwindowsuiawrapper_p.h"
|
||||
|
||||
#include <QtCore/qloggingcategory.h>
|
||||
#include <QtGui/private/qaccessiblebridgeutils_p.h>
|
||||
#include <QtGui/qaccessible.h>
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <qpa/qplatforminputcontextfactory_p.h>
|
||||
#include <QtCore/private/qcomvariant_p.h>
|
||||
|
||||
#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
|
||||
#include <comdef.h>
|
||||
#endif
|
||||
|
||||
#include <QtCore/qt_windows.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
using namespace QWindowsUiAutomation;
|
||||
|
||||
// Returns a cached instance of the provider for a specific accessible interface.
|
||||
ComPtr<QWindowsUiaMainProvider> QWindowsUiaMainProvider::providerForAccessible(QAccessibleInterface *accessible)
|
||||
{
|
||||
if (!accessible)
|
||||
return nullptr;
|
||||
|
||||
QAccessible::Id id = QAccessible::uniqueId(accessible);
|
||||
QWindowsUiaProviderCache *providerCache = QWindowsUiaProviderCache::instance();
|
||||
ComPtr<QWindowsUiaMainProvider> provider = providerCache->providerForId(id);
|
||||
|
||||
if (!provider) {
|
||||
provider = makeComObject<QWindowsUiaMainProvider>(accessible);
|
||||
providerCache->insert(id, provider.Get()); // Cache holds weak references
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
QWindowsUiaMainProvider::QWindowsUiaMainProvider(QAccessibleInterface *a)
|
||||
: QWindowsUiaBaseProvider(QAccessible::uniqueId(a))
|
||||
{
|
||||
}
|
||||
|
||||
QWindowsUiaMainProvider::~QWindowsUiaMainProvider()
|
||||
{
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::notifyFocusChange(QAccessibleEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
// If this is a complex element, raise event for the focused child instead.
|
||||
if (accessible->childCount()) {
|
||||
if (QAccessibleInterface *child = accessible->focusChild())
|
||||
accessible = child;
|
||||
}
|
||||
if (auto provider = providerForAccessible(accessible))
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(), UIA_AutomationFocusChangedEventId);
|
||||
}
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
if (event->changedStates().checked || event->changedStates().checkStateMixed) {
|
||||
// Notifies states changes in checkboxes.
|
||||
if (accessible->role() == QAccessible::CheckBox) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
long toggleState = ToggleState_Off;
|
||||
if (accessible->state().checked)
|
||||
toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
|
||||
|
||||
QComVariant oldVal;
|
||||
QComVariant newVal{toggleState};
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(
|
||||
provider.Get(), UIA_ToggleToggleStatePropertyId, oldVal.get(), newVal.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event->changedStates().active) {
|
||||
if (accessible->role() == QAccessible::Window) {
|
||||
// Notifies window opened/closed.
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
if (accessible->state().active) {
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(), UIA_Window_WindowOpenedEventId);
|
||||
if (QAccessibleInterface *focused = accessible->focusChild()) {
|
||||
if (auto focusedProvider = providerForAccessible(focused)) {
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(focusedProvider.Get(),
|
||||
UIA_AutomationFocusChangedEventId);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(), UIA_Window_WindowClosedEventId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
if (accessible->role() == QAccessible::ComboBox && accessible->childCount() > 0) {
|
||||
QAccessibleInterface *listacc = accessible->child(0);
|
||||
if (listacc && listacc->role() == QAccessible::List) {
|
||||
int count = listacc->childCount();
|
||||
for (int i = 0; i < count; ++i) {
|
||||
QAccessibleInterface *item = listacc->child(i);
|
||||
if (item && item->isValid() && item->text(QAccessible::Name) == event->value()) {
|
||||
if (!item->state().selected) {
|
||||
if (QAccessibleActionInterface *actionInterface = item->actionInterface())
|
||||
actionInterface->doAction(QAccessibleActionInterface::toggleAction());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (event->value().typeId() == QMetaType::QString) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
// Notifies changes in string values.
|
||||
const QComVariant oldVal;
|
||||
const QComVariant newVal{ event->value().toString() };
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider.Get(), UIA_ValueValuePropertyId,
|
||||
oldVal.get(), newVal.get());
|
||||
}
|
||||
} else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
// Notifies changes in values of controls supporting the value interface.
|
||||
const QComVariant oldVal;
|
||||
const QComVariant newVal{ valueInterface->currentValue().toDouble() };
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(
|
||||
provider.Get(), UIA_RangeValueValuePropertyId, oldVal.get(), newVal.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
// Restrict notification to combo boxes, which need it for accessibility,
|
||||
// in order to avoid slowdowns with unnecessary notifications.
|
||||
if (accessible->role() == QAccessible::ComboBox) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
QComVariant oldVal;
|
||||
QComVariant newVal{ accessible->text(QAccessible::Name) };
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationPropertyChangedEvent(provider.Get(), UIA_NamePropertyId,
|
||||
oldVal.get(), newVal.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::notifySelectionChange(QAccessibleEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
if (auto provider = providerForAccessible(accessible))
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(), UIA_SelectionItem_ElementSelectedEventId);
|
||||
}
|
||||
}
|
||||
|
||||
// Notifies changes in text content and selection state of text controls.
|
||||
void QWindowsUiaMainProvider::notifyTextChange(QAccessibleEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
if (accessible->textInterface()) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
if (event->type() == QAccessible::TextSelectionChanged) {
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(), UIA_Text_TextSelectionChangedEventId);
|
||||
} else if (event->type() == QAccessible::TextCaretMoved) {
|
||||
if (!accessible->state().readOnly) {
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(),
|
||||
UIA_Text_TextSelectionChangedEventId);
|
||||
}
|
||||
} else {
|
||||
QWindowsUiaWrapper::instance()->raiseAutomationEvent(provider.Get(), UIA_Text_TextChangedEventId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::raiseNotification(QAccessibleAnnouncementEvent *event)
|
||||
{
|
||||
if (QAccessibleInterface *accessible = event->accessibleInterface()) {
|
||||
if (auto provider = providerForAccessible(accessible)) {
|
||||
QBStr message{ event->message() };
|
||||
QAccessible::AnnouncementPoliteness prio = event->politeness();
|
||||
NotificationProcessing processing = (prio == QAccessible::AnnouncementPoliteness::Assertive)
|
||||
? NotificationProcessing_ImportantAll
|
||||
: NotificationProcessing_All;
|
||||
QBStr activityId{ QString::fromLatin1("") };
|
||||
QWindowsUiaWrapper::instance()->raiseNotificationEvent(provider.Get(), NotificationKind_Other, processing, message.bstr(),
|
||||
activityId.bstr());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT STDMETHODCALLTYPE QWindowsUiaMainProvider::QueryInterface(REFIID iid, LPVOID *iface)
|
||||
{
|
||||
HRESULT result = QComObject::QueryInterface(iid, iface);
|
||||
|
||||
if (SUCCEEDED(result) && iid == __uuidof(IRawElementProviderFragmentRoot)) {
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (accessible && hwndForAccessible(accessible)) {
|
||||
result = S_OK;
|
||||
} else {
|
||||
Release();
|
||||
result = E_NOINTERFACE;
|
||||
*iface = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaMainProvider::get_ProviderOptions(ProviderOptions *pRetVal)
|
||||
{
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
// We are STA, (OleInitialize()).
|
||||
*pRetVal = static_cast<ProviderOptions>(ProviderOptions_ServerSideProvider | ProviderOptions_UseComThreading);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Return providers for specific control patterns
|
||||
HRESULT QWindowsUiaMainProvider::GetPatternProvider(PATTERNID idPattern, IUnknown **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idPattern;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
switch (idPattern) {
|
||||
case UIA_WindowPatternId:
|
||||
if (accessible->parent() && (accessible->parent()->role() == QAccessible::Application)) {
|
||||
*pRetVal = makeComObject<QWindowsUiaWindowProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_TextPatternId:
|
||||
case UIA_TextPattern2Id:
|
||||
// All text controls.
|
||||
if (accessible->textInterface()) {
|
||||
*pRetVal = makeComObject<QWindowsUiaTextProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_ValuePatternId:
|
||||
// All non-static controls support the Value pattern.
|
||||
if (accessible->role() != QAccessible::StaticText)
|
||||
*pRetVal = makeComObject<QWindowsUiaValueProvider>(id()).Detach();
|
||||
break;
|
||||
case UIA_RangeValuePatternId:
|
||||
// Controls providing a numeric value within a range (e.g., sliders, scroll bars, dials).
|
||||
if (accessible->valueInterface()) {
|
||||
*pRetVal = makeComObject<QWindowsUiaRangeValueProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_TogglePatternId:
|
||||
// Checkboxes and other checkable controls.
|
||||
if (accessible->state().checkable)
|
||||
*pRetVal = makeComObject<QWindowsUiaToggleProvider>(id()).Detach();
|
||||
break;
|
||||
case UIA_SelectionPatternId:
|
||||
case UIA_SelectionPattern2Id:
|
||||
// Selections via QAccessibleSelectionInterface or lists of items.
|
||||
if (accessible->selectionInterface()
|
||||
|| accessible->role() == QAccessible::List
|
||||
|| accessible->role() == QAccessible::PageTabList) {
|
||||
*pRetVal = makeComObject<QWindowsUiaSelectionProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_SelectionItemPatternId:
|
||||
// Parent supports selection interface or items within a list and radio buttons.
|
||||
if ((accessible->parent() && accessible->parent()->selectionInterface())
|
||||
|| (accessible->role() == QAccessible::RadioButton)
|
||||
|| (accessible->role() == QAccessible::ListItem)
|
||||
|| (accessible->role() == QAccessible::PageTab)) {
|
||||
*pRetVal = makeComObject<QWindowsUiaSelectionItemProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_TablePatternId:
|
||||
// Table/tree.
|
||||
if (accessible->tableInterface()
|
||||
&& ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
|
||||
*pRetVal = makeComObject<QWindowsUiaTableProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_TableItemPatternId:
|
||||
// Item within a table/tree.
|
||||
if (accessible->tableCellInterface()
|
||||
&& ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
|
||||
*pRetVal = makeComObject<QWindowsUiaTableItemProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_GridPatternId:
|
||||
// Table/tree.
|
||||
if (accessible->tableInterface()
|
||||
&& ((accessible->role() == QAccessible::Table) || (accessible->role() == QAccessible::Tree))) {
|
||||
*pRetVal = makeComObject<QWindowsUiaGridProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_GridItemPatternId:
|
||||
// Item within a table/tree.
|
||||
if (accessible->tableCellInterface()
|
||||
&& ((accessible->role() == QAccessible::Cell) || (accessible->role() == QAccessible::TreeItem))) {
|
||||
*pRetVal = makeComObject<QWindowsUiaGridItemProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_InvokePatternId:
|
||||
// Things that have an invokable action (e.g., simple buttons).
|
||||
if (accessible->actionInterface()) {
|
||||
*pRetVal = makeComObject<QWindowsUiaInvokeProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
case UIA_ExpandCollapsePatternId:
|
||||
// Menu items with submenus.
|
||||
if ((accessible->role() == QAccessible::MenuItem
|
||||
&& accessible->childCount() > 0
|
||||
&& accessible->child(0)->role() == QAccessible::PopupMenu)
|
||||
|| accessible->role() == QAccessible::ComboBox
|
||||
|| (accessible->role() == QAccessible::TreeItem && accessible->state().expandable)) {
|
||||
*pRetVal = makeComObject<QWindowsUiaExpandCollapseProvider>(id()).Detach();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::fillVariantArrayForRelation(QAccessibleInterface* accessible,
|
||||
QAccessible::Relation relation, VARIANT *pRetVal)
|
||||
{
|
||||
Q_ASSERT(accessible);
|
||||
|
||||
typedef QPair<QAccessibleInterface*, QAccessible::Relation> RelationPair;
|
||||
const QList<RelationPair> relationInterfaces = accessible->relations(relation);
|
||||
if (relationInterfaces.empty())
|
||||
return;
|
||||
|
||||
SAFEARRAY *elements = SafeArrayCreateVector(VT_UNKNOWN, 0, relationInterfaces.size());
|
||||
for (LONG i = 0; i < relationInterfaces.size(); ++i) {
|
||||
if (ComPtr<IRawElementProviderSimple> provider =
|
||||
providerForAccessible(relationInterfaces.at(i).first)) {
|
||||
SafeArrayPutElement(elements, &i, provider.Get());
|
||||
}
|
||||
}
|
||||
|
||||
pRetVal->vt = VT_UNKNOWN | VT_ARRAY;
|
||||
pRetVal->parray = elements;
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::setAriaProperties(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
||||
{
|
||||
Q_ASSERT(accessible);
|
||||
|
||||
QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface();
|
||||
if (!attributesIface)
|
||||
return;
|
||||
|
||||
QString ariaString;
|
||||
const QList<QAccessible::Attribute> attrKeys = attributesIface->attributeKeys();
|
||||
for (qsizetype i = 0; i < attrKeys.size(); ++i) {
|
||||
if (i != 0)
|
||||
ariaString += QStringLiteral(";");
|
||||
const QAccessible::Attribute key = attrKeys.at(i);
|
||||
const QVariant value = attributesIface->attributeValue(key);
|
||||
// see "Core Accessibility API Mappings" spec: https://www.w3.org/TR/core-aam-1.2/
|
||||
switch (key) {
|
||||
case QAccessible::Attribute::Custom:
|
||||
{
|
||||
// forward custom attributes as-is
|
||||
Q_ASSERT((value.canConvert<QHash<QString, QString>>()));
|
||||
const QHash<QString, QString> attrMap = value.value<QHash<QString, QString>>();
|
||||
for (auto [name, val] : attrMap.asKeyValueRange()) {
|
||||
if (name != *attrMap.keyBegin())
|
||||
ariaString += QStringLiteral(";");
|
||||
ariaString += name + QStringLiteral("=") + val;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QAccessible::Attribute::Level:
|
||||
Q_ASSERT(value.canConvert<int>());
|
||||
ariaString += QStringLiteral("level=") + QString::number(value.toInt());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
*pRetVal = QComVariant{ ariaString }.release();
|
||||
}
|
||||
|
||||
void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
||||
{
|
||||
Q_ASSERT(accessible);
|
||||
|
||||
QAccessibleAttributesInterface *attributesIface = accessible->attributesInterface();
|
||||
if (!attributesIface)
|
||||
return;
|
||||
|
||||
// currently, only heading styles are implemented here
|
||||
if (accessible->role() != QAccessible::Role::Heading)
|
||||
return;
|
||||
|
||||
const QVariant levelVariant = attributesIface->attributeValue(QAccessible::Attribute::Level);
|
||||
if (!levelVariant.isValid())
|
||||
return;
|
||||
|
||||
Q_ASSERT(levelVariant.canConvert<int>());
|
||||
// UIA only has styles for heading levels 1-9
|
||||
const int level = levelVariant.toInt();
|
||||
if (level < 1 || level > 9)
|
||||
return;
|
||||
|
||||
const long styleId = styleIdForHeadingLevel(level);
|
||||
*pRetVal = QComVariant{ styleId }.release();
|
||||
}
|
||||
|
||||
int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel)
|
||||
{
|
||||
// only heading levels 1-9 have a corresponding UIA style ID
|
||||
Q_ASSERT(headingLevel > 0 && headingLevel <= 9);
|
||||
|
||||
static constexpr int styles[] = {
|
||||
StyleId_Heading1,
|
||||
StyleId_Heading2,
|
||||
StyleId_Heading3,
|
||||
StyleId_Heading4,
|
||||
StyleId_Heading5,
|
||||
StyleId_Heading6,
|
||||
StyleId_Heading7,
|
||||
StyleId_Heading8,
|
||||
StyleId_Heading9,
|
||||
};
|
||||
|
||||
return styles[headingLevel - 1];
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << idProp;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
clearVariant(pRetVal);
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
bool topLevelWindow = accessible->parent() && (accessible->parent()->role() == QAccessible::Application);
|
||||
|
||||
switch (idProp) {
|
||||
case UIA_ProcessIdPropertyId:
|
||||
// PID
|
||||
*pRetVal = QComVariant{ static_cast<long>(GetCurrentProcessId()) }.release();
|
||||
break;
|
||||
case UIA_AccessKeyPropertyId:
|
||||
// Accelerator key.
|
||||
*pRetVal = QComVariant{ accessible->text(QAccessible::Accelerator) }.release();
|
||||
break;
|
||||
case UIA_AriaPropertiesPropertyId:
|
||||
setAriaProperties(accessible, pRetVal);
|
||||
break;
|
||||
case UIA_AutomationIdPropertyId:
|
||||
// Automation ID, which can be used by tools to select a specific control in the UI.
|
||||
*pRetVal = QComVariant{ QAccessibleBridgeUtils::accessibleId(accessible) }.release();
|
||||
break;
|
||||
case UIA_ClassNamePropertyId:
|
||||
// Class name.
|
||||
if (QObject *o = accessible->object()) {
|
||||
QString className = QLatin1StringView(o->metaObject()->className());
|
||||
*pRetVal = QComVariant{ className }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_DescribedByPropertyId:
|
||||
fillVariantArrayForRelation(accessible, QAccessible::DescriptionFor, pRetVal);
|
||||
break;
|
||||
case UIA_FlowsFromPropertyId:
|
||||
fillVariantArrayForRelation(accessible, QAccessible::FlowsTo, pRetVal);
|
||||
break;
|
||||
case UIA_FlowsToPropertyId:
|
||||
fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal);
|
||||
break;
|
||||
case UIA_FrameworkIdPropertyId:
|
||||
*pRetVal = QComVariant{ QStringLiteral("Qt") }.release();
|
||||
break;
|
||||
case UIA_ControlTypePropertyId:
|
||||
if (topLevelWindow) {
|
||||
// Reports a top-level widget as a window, instead of "custom".
|
||||
*pRetVal = QComVariant{ UIA_WindowControlTypeId }.release();
|
||||
} else {
|
||||
// Control type converted from role.
|
||||
auto controlType = roleToControlTypeId(accessible->role());
|
||||
|
||||
// The native OSK should be disabled if the Qt OSK is in use,
|
||||
// or if disabled via application attribute.
|
||||
static bool imModuleEmpty = QPlatformInputContextFactory::requested().isEmpty();
|
||||
bool nativeVKDisabled = QCoreApplication::testAttribute(Qt::AA_DisableNativeVirtualKeyboard);
|
||||
|
||||
// If we want to disable the native OSK auto-showing
|
||||
// we have to report text fields as non-editable.
|
||||
if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled))
|
||||
controlType = UIA_TextControlTypeId;
|
||||
|
||||
*pRetVal = QComVariant{ controlType }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_HelpTextPropertyId:
|
||||
*pRetVal = QComVariant{ accessible->text(QAccessible::Help) }.release();
|
||||
break;
|
||||
case UIA_HasKeyboardFocusPropertyId:
|
||||
if (topLevelWindow) {
|
||||
// Windows set the active state to true when they are focused
|
||||
*pRetVal = QComVariant{ accessible->state().active ? true : false }.release();
|
||||
} else {
|
||||
*pRetVal = QComVariant{ accessible->state().focused ? true : false }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_IsKeyboardFocusablePropertyId:
|
||||
if (topLevelWindow) {
|
||||
// Windows should always be focusable
|
||||
*pRetVal = QComVariant{ true }.release();
|
||||
} else {
|
||||
*pRetVal = QComVariant{ accessible->state().focusable ? true : false }.release();
|
||||
}
|
||||
break;
|
||||
case UIA_IsOffscreenPropertyId:
|
||||
*pRetVal = QComVariant{ accessible->state().offscreen ? true : false }.release();
|
||||
break;
|
||||
case UIA_IsContentElementPropertyId:
|
||||
*pRetVal = QComVariant{ true }.release();
|
||||
break;
|
||||
case UIA_IsControlElementPropertyId:
|
||||
*pRetVal = QComVariant{ true }.release();
|
||||
break;
|
||||
case UIA_IsEnabledPropertyId:
|
||||
*pRetVal = QComVariant{ !accessible->state().disabled }.release();
|
||||
break;
|
||||
case UIA_IsPasswordPropertyId:
|
||||
*pRetVal = QComVariant{ accessible->role() == QAccessible::EditableText
|
||||
&& accessible->state().passwordEdit }
|
||||
.release();
|
||||
break;
|
||||
case UIA_IsPeripheralPropertyId:
|
||||
// True for peripheral UIs.
|
||||
if (QWindow *window = windowForAccessible(accessible)) {
|
||||
const Qt::WindowType wt = window->type();
|
||||
*pRetVal = QComVariant{ wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen }
|
||||
.release();
|
||||
}
|
||||
break;
|
||||
case UIA_IsDialogPropertyId:
|
||||
*pRetVal = QComVariant{ accessible->role() == QAccessible::Dialog
|
||||
|| accessible->role() == QAccessible::AlertMessage }
|
||||
.release();
|
||||
break;
|
||||
case UIA_FullDescriptionPropertyId:
|
||||
*pRetVal = QComVariant{ accessible->text(QAccessible::Description) }.release();
|
||||
break;
|
||||
case UIA_NamePropertyId: {
|
||||
QString name = accessible->text(QAccessible::Name);
|
||||
if (name.isEmpty() && topLevelWindow)
|
||||
name = QCoreApplication::applicationName();
|
||||
*pRetVal = QComVariant{ name }.release();
|
||||
break;
|
||||
}
|
||||
case UIA_StyleIdAttributeId:
|
||||
setStyle(accessible, pRetVal);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaMainProvider::get_HostRawElementProvider(IRawElementProviderSimple **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
// Returns a host provider only for controls associated with a native window handle. Others should return NULL.
|
||||
if (QAccessibleInterface *accessible = accessibleInterface()) {
|
||||
if (HWND hwnd = hwndForAccessible(accessible)) {
|
||||
return QWindowsUiaWrapper::instance()->hostProviderFromHwnd(hwnd, pRetVal);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Navigates within the tree of accessible controls.
|
||||
HRESULT QWindowsUiaMainProvider::Navigate(NavigateDirection direction, IRawElementProviderFragment **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << direction << " this: " << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QAccessibleInterface *targetacc = nullptr;
|
||||
|
||||
if (direction == NavigateDirection_Parent) {
|
||||
if (QAccessibleInterface *parent = accessible->parent()) {
|
||||
// The Application's children are considered top level objects.
|
||||
if (parent->isValid() && parent->role() != QAccessible::Application) {
|
||||
targetacc = parent;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
QAccessibleInterface *parent = nullptr;
|
||||
int index = 0;
|
||||
int incr = 1;
|
||||
switch (direction) {
|
||||
case NavigateDirection_FirstChild:
|
||||
parent = accessible;
|
||||
index = 0;
|
||||
incr = 1;
|
||||
break;
|
||||
case NavigateDirection_LastChild:
|
||||
parent = accessible;
|
||||
index = accessible->childCount() - 1;
|
||||
incr = -1;
|
||||
break;
|
||||
case NavigateDirection_NextSibling:
|
||||
if ((parent = accessible->parent()))
|
||||
index = parent->indexOfChild(accessible) + 1;
|
||||
incr = 1;
|
||||
break;
|
||||
case NavigateDirection_PreviousSibling:
|
||||
if ((parent = accessible->parent()))
|
||||
index = parent->indexOfChild(accessible) - 1;
|
||||
incr = -1;
|
||||
break;
|
||||
default:
|
||||
Q_UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
if (parent && parent->isValid()) {
|
||||
for (int count = parent->childCount(); index >= 0 && index < count; index += incr) {
|
||||
if (QAccessibleInterface *child = parent->child(index)) {
|
||||
if (child->isValid() && !child->state().invisible) {
|
||||
targetacc = child;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetacc)
|
||||
*pRetVal = providerForAccessible(targetacc).Detach();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Returns a unique id assigned to the UI element, used as key by the UI Automation framework.
|
||||
HRESULT QWindowsUiaMainProvider::GetRuntimeId(SAFEARRAY **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
// The UiaAppendRuntimeId constant is used to make then ID unique
|
||||
// among multiple instances running on the system.
|
||||
int rtId[] = { UiaAppendRuntimeId, int(id()) };
|
||||
|
||||
if ((*pRetVal = SafeArrayCreateVector(VT_I4, 0, 2))) {
|
||||
for (LONG i = 0; i < 2; ++i)
|
||||
SafeArrayPutElement(*pRetVal, &i, &rtId[i]);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Returns the bounding rectangle for the accessible control.
|
||||
HRESULT QWindowsUiaMainProvider::get_BoundingRectangle(UiaRect *pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QWindow *window = windowForAccessible(accessible);
|
||||
if (!window)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
rectToNativeUiaRect(accessible->rect(), window, pRetVal);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaMainProvider::GetEmbeddedFragmentRoots(SAFEARRAY **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
// No embedded roots.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Sets focus to the control.
|
||||
HRESULT QWindowsUiaMainProvider::SetFocus()
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QAccessibleActionInterface *actionInterface = accessible->actionInterface();
|
||||
if (!actionInterface)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
actionInterface->doAction(QAccessibleActionInterface::setFocusAction());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaMainProvider::get_FragmentRoot(IRawElementProviderFragmentRoot **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
// Our UI Automation implementation considers the window as the root for
|
||||
// non-native controls/fragments.
|
||||
if (QAccessibleInterface *accessible = accessibleInterface()) {
|
||||
if (QWindow *window = windowForAccessible(accessible)) {
|
||||
if (QAccessibleInterface *rootacc = window->accessibleRoot())
|
||||
*pRetVal = providerForAccessible(rootacc).Detach();
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Returns a provider for the UI element present at the specified screen coordinates.
|
||||
HRESULT QWindowsUiaMainProvider::ElementProviderFromPoint(double x, double y, IRawElementProviderFragment **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << x << y;
|
||||
|
||||
if (!pRetVal) {
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
*pRetVal = nullptr;
|
||||
|
||||
QAccessibleInterface *accessible = accessibleInterface();
|
||||
if (!accessible)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
QWindow *window = windowForAccessible(accessible);
|
||||
if (!window)
|
||||
return UIA_E_ELEMENTNOTAVAILABLE;
|
||||
|
||||
// Scales coordinates from High DPI screens.
|
||||
UiaPoint uiaPoint = {x, y};
|
||||
QPoint point;
|
||||
nativeUiaPointToPoint(uiaPoint, window, &point);
|
||||
|
||||
QAccessibleInterface *targetacc = accessible->childAt(point.x(), point.y());
|
||||
|
||||
if (targetacc) {
|
||||
QAccessibleInterface *acc = targetacc;
|
||||
// Controls can be embedded within grouping elements. By default returns the innermost control.
|
||||
while (acc) {
|
||||
targetacc = acc;
|
||||
// For accessibility tools it may be better to return the text element instead of its subcomponents.
|
||||
if (targetacc->textInterface()) break;
|
||||
acc = acc->childAt(point.x(), point.y());
|
||||
}
|
||||
*pRetVal = providerForAccessible(targetacc).Detach();
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Returns the fragment with focus.
|
||||
HRESULT QWindowsUiaMainProvider::GetFocus(IRawElementProviderFragment **pRetVal)
|
||||
{
|
||||
qCDebug(lcQpaUiAutomation) << __FUNCTION__ << this;
|
||||
|
||||
if (!pRetVal)
|
||||
return E_INVALIDARG;
|
||||
*pRetVal = nullptr;
|
||||
|
||||
if (QAccessibleInterface *accessible = accessibleInterface()) {
|
||||
if (QAccessibleInterface *focusacc = accessible->focusChild()) {
|
||||
*pRetVal = providerForAccessible(focusacc).Detach();
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QT_CONFIG(accessibility)
|
@ -0,0 +1,89 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include <initguid.h>
|
||||
|
||||
#include "qwindowsuiawrapper_p.h"
|
||||
#include <QtCore/private/qsystemlibrary_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
// private constructor
|
||||
QWindowsUiaWrapper::QWindowsUiaWrapper()
|
||||
{
|
||||
QSystemLibrary uiaLib(QStringLiteral("UIAutomationCore"));
|
||||
if (uiaLib.load()) {
|
||||
m_pUiaReturnRawElementProvider = reinterpret_cast<PtrUiaReturnRawElementProvider>(uiaLib.resolve("UiaReturnRawElementProvider"));
|
||||
m_pUiaHostProviderFromHwnd = reinterpret_cast<PtrUiaHostProviderFromHwnd>(uiaLib.resolve("UiaHostProviderFromHwnd"));
|
||||
m_pUiaRaiseAutomationPropertyChangedEvent = reinterpret_cast<PtrUiaRaiseAutomationPropertyChangedEvent>(uiaLib.resolve("UiaRaiseAutomationPropertyChangedEvent"));
|
||||
m_pUiaRaiseAutomationEvent = reinterpret_cast<PtrUiaRaiseAutomationEvent>(uiaLib.resolve("UiaRaiseAutomationEvent"));
|
||||
m_pUiaRaiseNotificationEvent = reinterpret_cast<PtrUiaRaiseNotificationEvent>(uiaLib.resolve("UiaRaiseNotificationEvent"));
|
||||
m_pUiaClientsAreListening = reinterpret_cast<PtrUiaClientsAreListening>(uiaLib.resolve("UiaClientsAreListening"));
|
||||
}
|
||||
}
|
||||
|
||||
QWindowsUiaWrapper::~QWindowsUiaWrapper()
|
||||
{
|
||||
}
|
||||
|
||||
// shared instance
|
||||
QWindowsUiaWrapper *QWindowsUiaWrapper::instance()
|
||||
{
|
||||
static QWindowsUiaWrapper wrapper;
|
||||
return &wrapper;
|
||||
}
|
||||
|
||||
// True if all symbols resolved.
|
||||
BOOL QWindowsUiaWrapper::ready()
|
||||
{
|
||||
return m_pUiaReturnRawElementProvider
|
||||
&& m_pUiaHostProviderFromHwnd
|
||||
&& m_pUiaRaiseAutomationPropertyChangedEvent
|
||||
&& m_pUiaRaiseAutomationEvent
|
||||
&& m_pUiaClientsAreListening;
|
||||
}
|
||||
|
||||
BOOL QWindowsUiaWrapper::clientsAreListening()
|
||||
{
|
||||
if (!m_pUiaClientsAreListening)
|
||||
return FALSE;
|
||||
return m_pUiaClientsAreListening();
|
||||
}
|
||||
|
||||
LRESULT QWindowsUiaWrapper::returnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *el)
|
||||
{
|
||||
if (!m_pUiaReturnRawElementProvider)
|
||||
return static_cast<LRESULT>(NULL);
|
||||
return m_pUiaReturnRawElementProvider(hwnd, wParam, lParam, el);
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaWrapper::hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider)
|
||||
{
|
||||
if (!m_pUiaHostProviderFromHwnd)
|
||||
return UIA_E_NOTSUPPORTED;
|
||||
return m_pUiaHostProviderFromHwnd(hwnd, ppProvider);
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaWrapper::raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue)
|
||||
{
|
||||
if (!m_pUiaRaiseAutomationPropertyChangedEvent)
|
||||
return UIA_E_NOTSUPPORTED;
|
||||
return m_pUiaRaiseAutomationPropertyChangedEvent(pProvider, id, oldValue, newValue);
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaWrapper::raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id)
|
||||
{
|
||||
if (!m_pUiaRaiseAutomationEvent)
|
||||
return UIA_E_NOTSUPPORTED;
|
||||
return m_pUiaRaiseAutomationEvent(pProvider, id);
|
||||
}
|
||||
|
||||
HRESULT QWindowsUiaWrapper::raiseNotificationEvent(IRawElementProviderSimple *pProvider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId)
|
||||
{
|
||||
if (!m_pUiaRaiseNotificationEvent)
|
||||
return UIA_E_NOTSUPPORTED;
|
||||
return m_pUiaRaiseNotificationEvent(pProvider, notificationKind, notificationProcessing, displayString, activityId);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -0,0 +1,57 @@
|
||||
// Copyright (C) 2017 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#ifndef QWINDOWSUIAWRAPPER_H
|
||||
#define QWINDOWSUIAWRAPPER_H
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of other Qt classes. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#include <QtGui/private/qtguiglobal_p.h>
|
||||
|
||||
QT_REQUIRE_CONFIG(accessibility);
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QWindowsUiaWrapper
|
||||
{
|
||||
QWindowsUiaWrapper();
|
||||
virtual ~QWindowsUiaWrapper();
|
||||
public:
|
||||
static QWindowsUiaWrapper *instance();
|
||||
BOOL ready();
|
||||
BOOL clientsAreListening();
|
||||
LRESULT returnRawElementProvider(HWND hwnd, WPARAM wParam, LPARAM lParam, IRawElementProviderSimple *el);
|
||||
HRESULT hostProviderFromHwnd(HWND hwnd, IRawElementProviderSimple **ppProvider);
|
||||
HRESULT raiseAutomationPropertyChangedEvent(IRawElementProviderSimple *pProvider, PROPERTYID id, VARIANT oldValue, VARIANT newValue);
|
||||
HRESULT raiseAutomationEvent(IRawElementProviderSimple *pProvider, EVENTID id);
|
||||
HRESULT raiseNotificationEvent(IRawElementProviderSimple *pProvider, NotificationKind notificationKind, NotificationProcessing notificationProcessing, BSTR displayString, BSTR activityId);
|
||||
|
||||
private:
|
||||
typedef LRESULT (WINAPI *PtrUiaReturnRawElementProvider)(HWND, WPARAM, LPARAM, IRawElementProviderSimple *);
|
||||
typedef HRESULT (WINAPI *PtrUiaHostProviderFromHwnd)(HWND, IRawElementProviderSimple **);
|
||||
typedef HRESULT (WINAPI *PtrUiaRaiseAutomationPropertyChangedEvent)(IRawElementProviderSimple *, PROPERTYID, VARIANT, VARIANT);
|
||||
typedef HRESULT (WINAPI *PtrUiaRaiseAutomationEvent)(IRawElementProviderSimple *, EVENTID);
|
||||
typedef HRESULT (WINAPI *PtrUiaRaiseNotificationEvent)(IRawElementProviderSimple *, NotificationKind, NotificationProcessing, BSTR, BSTR);
|
||||
|
||||
typedef BOOL (WINAPI *PtrUiaClientsAreListening)();
|
||||
PtrUiaReturnRawElementProvider m_pUiaReturnRawElementProvider = nullptr;
|
||||
PtrUiaHostProviderFromHwnd m_pUiaHostProviderFromHwnd = nullptr;
|
||||
PtrUiaRaiseAutomationPropertyChangedEvent m_pUiaRaiseAutomationPropertyChangedEvent = nullptr;
|
||||
PtrUiaRaiseAutomationEvent m_pUiaRaiseAutomationEvent = nullptr;
|
||||
PtrUiaRaiseNotificationEvent m_pUiaRaiseNotificationEvent = nullptr;
|
||||
PtrUiaClientsAreListening m_pUiaClientsAreListening = nullptr;
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif //QWINDOWSUIAWRAPPER_H
|
||||
|
Reference in New Issue
Block a user