Simplify VARIANT handling using a QComVariant RAII wrapper
The QComVariant RAII wrapper automates lifetime management of VARIANT structures and elements that it contains. For example, it will call IUnknown::AddRef if a COM interface is assigned to it. It will also clear all held resources at destruction. Task-number: QTBUG-126530 Pick-to: 6.8 Change-Id: I543d236293d25cbc70ff25046e553351eccfb850 Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
parent
93686386c0
commit
717580c198
@ -565,6 +565,7 @@ qt_internal_extend_target(Core CONDITION WIN32
|
|||||||
platform/windows/qcomobject_p.h
|
platform/windows/qcomobject_p.h
|
||||||
platform/windows/qcomptr_p.h
|
platform/windows/qcomptr_p.h
|
||||||
platform/windows/qbstr_p.h
|
platform/windows/qbstr_p.h
|
||||||
|
platform/windows/qcomvariant_p.h
|
||||||
LIBRARIES
|
LIBRARIES
|
||||||
advapi32
|
advapi32
|
||||||
authz
|
authz
|
||||||
|
134
src/corelib/platform/windows/qcomvariant_p.h
Normal file
134
src/corelib/platform/windows/qcomvariant_p.h
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
// Copyright (C) 2024 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 QCOMVARIANT_P_H
|
||||||
|
#define QCOMVARIANT_P_H
|
||||||
|
|
||||||
|
//
|
||||||
|
// W A R N I N G
|
||||||
|
// -------------
|
||||||
|
//
|
||||||
|
// This file is not part of the Qt API. It exists purely as an
|
||||||
|
// implementation detail. This header file may change from version to
|
||||||
|
// version without notice, or even be removed.
|
||||||
|
//
|
||||||
|
// We mean it.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if defined(Q_OS_WIN) || defined(Q_QDOC)
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
#include <QtCore/private/qglobal_p.h>
|
||||||
|
#include <QtCore/qt_windows.h>
|
||||||
|
#include <QtCore/private/qbstr_p.h>
|
||||||
|
#include <QtCore/private/qcomptr_p.h>
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
struct QComVariant
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
QComVariant() noexcept
|
||||||
|
{
|
||||||
|
VariantInit(&m_variant);
|
||||||
|
}
|
||||||
|
|
||||||
|
~QComVariant() noexcept
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit QComVariant(bool value) noexcept
|
||||||
|
{
|
||||||
|
m_variant.vt = VT_BOOL;
|
||||||
|
m_variant.boolVal = value ? VARIANT_TRUE : VARIANT_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit QComVariant(int value) noexcept
|
||||||
|
{
|
||||||
|
m_variant.vt = VT_INT;
|
||||||
|
m_variant.intVal = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit QComVariant(long value) noexcept
|
||||||
|
{
|
||||||
|
m_variant.vt = VT_I4;
|
||||||
|
m_variant.lVal = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
explicit QComVariant(double value) noexcept
|
||||||
|
{
|
||||||
|
m_variant.vt = VT_R8;
|
||||||
|
m_variant.dblVal = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
QComVariant(const ComPtr<T> &value) noexcept
|
||||||
|
{
|
||||||
|
static_assert(std::is_base_of_v<IUnknown, T>, "Invalid COM interface");
|
||||||
|
ComPtr<IUnknown> unknown = value;
|
||||||
|
m_variant.vt = VT_UNKNOWN;
|
||||||
|
m_variant.punkVal = unknown.Detach(); // Transfer ownership
|
||||||
|
}
|
||||||
|
|
||||||
|
QComVariant(QBStr &&value) noexcept
|
||||||
|
{
|
||||||
|
m_variant.vt = VT_BSTR;
|
||||||
|
m_variant.bstrVal = value.release(); // Transfer ownership
|
||||||
|
}
|
||||||
|
|
||||||
|
QComVariant(const QString &str)
|
||||||
|
{
|
||||||
|
m_variant.vt = VT_BSTR;
|
||||||
|
m_variant.bstrVal = QBStr{ str }.release(); // Transfer ownership of copy
|
||||||
|
}
|
||||||
|
|
||||||
|
const VARIANT &get() const noexcept
|
||||||
|
{
|
||||||
|
return m_variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
VARIANT &get() noexcept
|
||||||
|
{
|
||||||
|
return m_variant;
|
||||||
|
}
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
[[nodiscard]] VARIANT *operator&() noexcept // NOLINT(google-runtime-operator)
|
||||||
|
{
|
||||||
|
clear();
|
||||||
|
return &m_variant;
|
||||||
|
}
|
||||||
|
|
||||||
|
VARIANT release() noexcept
|
||||||
|
{
|
||||||
|
const VARIANT detached{ m_variant };
|
||||||
|
VariantInit(&m_variant);
|
||||||
|
return detached;
|
||||||
|
}
|
||||||
|
|
||||||
|
QComVariant(const QComVariant &) = delete;
|
||||||
|
QComVariant &operator=(const QComVariant &) = delete;
|
||||||
|
QComVariant(QComVariant && other) = delete;
|
||||||
|
QComVariant &operator=(QComVariant &&) = delete;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
const HRESULT hr = VariantClear(&m_variant);
|
||||||
|
Q_ASSERT(hr == S_OK);
|
||||||
|
Q_UNUSED(hr)
|
||||||
|
|
||||||
|
VariantInit(&m_variant); // Clear value field
|
||||||
|
}
|
||||||
|
|
||||||
|
VARIANT m_variant{};
|
||||||
|
};
|
||||||
|
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // Q_OS_WIN
|
||||||
|
|
||||||
|
#endif // QCOMVARIANT_P_H
|
@ -28,6 +28,7 @@
|
|||||||
#include <QtGui/qguiapplication.h>
|
#include <QtGui/qguiapplication.h>
|
||||||
#include <QtGui/qwindow.h>
|
#include <QtGui/qwindow.h>
|
||||||
#include <qpa/qplatforminputcontextfactory_p.h>
|
#include <qpa/qplatforminputcontextfactory_p.h>
|
||||||
|
#include <QtCore/private/qcomvariant_p.h>
|
||||||
|
|
||||||
#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
|
#if !defined(Q_CC_BOR) && !defined (Q_CC_GNU)
|
||||||
#include <comdef.h>
|
#include <comdef.h>
|
||||||
@ -85,14 +86,14 @@ void QWindowsUiaMainProvider::notifyStateChange(QAccessibleStateChangeEvent *eve
|
|||||||
// Notifies states changes in checkboxes.
|
// Notifies states changes in checkboxes.
|
||||||
if (accessible->role() == QAccessible::CheckBox) {
|
if (accessible->role() == QAccessible::CheckBox) {
|
||||||
if (auto provider = providerForAccessible(accessible)) {
|
if (auto provider = providerForAccessible(accessible)) {
|
||||||
VARIANT oldVal, newVal;
|
long toggleState = ToggleState_Off;
|
||||||
clearVariant(&oldVal);
|
|
||||||
int toggleState = ToggleState_Off;
|
|
||||||
if (accessible->state().checked)
|
if (accessible->state().checked)
|
||||||
toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
|
toggleState = accessible->state().checkStateMixed ? ToggleState_Indeterminate : ToggleState_On;
|
||||||
setVariantI4(toggleState, &newVal);
|
|
||||||
|
QComVariant oldVal;
|
||||||
|
QComVariant newVal{toggleState};
|
||||||
UiaRaiseAutomationPropertyChangedEvent(
|
UiaRaiseAutomationPropertyChangedEvent(
|
||||||
provider.Get(), UIA_ToggleToggleStatePropertyId, oldVal, newVal);
|
provider.Get(), UIA_ToggleToggleStatePropertyId, oldVal.get(), newVal.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -139,24 +140,18 @@ void QWindowsUiaMainProvider::notifyValueChange(QAccessibleValueChangeEvent *eve
|
|||||||
if (event->value().typeId() == QMetaType::QString) {
|
if (event->value().typeId() == QMetaType::QString) {
|
||||||
if (auto provider = providerForAccessible(accessible)) {
|
if (auto provider = providerForAccessible(accessible)) {
|
||||||
// Notifies changes in string values.
|
// Notifies changes in string values.
|
||||||
VARIANT oldVal, newVal;
|
const QComVariant oldVal;
|
||||||
clearVariant(&oldVal);
|
const QComVariant newVal{ event->value().toString() };
|
||||||
setVariantString(event->value().toString(), &newVal);
|
|
||||||
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_ValueValuePropertyId,
|
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_ValueValuePropertyId,
|
||||||
oldVal, newVal);
|
oldVal.get(), newVal.get());
|
||||||
|
|
||||||
HRESULT hr = VariantClear(&newVal); // Free string allocated by setVariantString
|
|
||||||
Q_ASSERT(hr == S_OK);
|
|
||||||
Q_UNUSED(hr)
|
|
||||||
}
|
}
|
||||||
} else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
|
} else if (QAccessibleValueInterface *valueInterface = accessible->valueInterface()) {
|
||||||
if (auto provider = providerForAccessible(accessible)) {
|
if (auto provider = providerForAccessible(accessible)) {
|
||||||
// Notifies changes in values of controls supporting the value interface.
|
// Notifies changes in values of controls supporting the value interface.
|
||||||
VARIANT oldVal, newVal;
|
const QComVariant oldVal;
|
||||||
clearVariant(&oldVal);
|
const QComVariant newVal{ valueInterface->currentValue().toDouble() };
|
||||||
setVariantDouble(valueInterface->currentValue().toDouble(), &newVal);
|
|
||||||
UiaRaiseAutomationPropertyChangedEvent(
|
UiaRaiseAutomationPropertyChangedEvent(
|
||||||
provider.Get(), UIA_RangeValueValuePropertyId, oldVal, newVal);
|
provider.Get(), UIA_RangeValueValuePropertyId, oldVal.get(), newVal.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -169,12 +164,10 @@ void QWindowsUiaMainProvider::notifyNameChange(QAccessibleEvent *event)
|
|||||||
// in order to avoid slowdowns with unnecessary notifications.
|
// in order to avoid slowdowns with unnecessary notifications.
|
||||||
if (accessible->role() == QAccessible::ComboBox) {
|
if (accessible->role() == QAccessible::ComboBox) {
|
||||||
if (auto provider = providerForAccessible(accessible)) {
|
if (auto provider = providerForAccessible(accessible)) {
|
||||||
VARIANT oldVal, newVal;
|
QComVariant oldVal;
|
||||||
clearVariant(&oldVal);
|
QComVariant newVal{ accessible->text(QAccessible::Name) };
|
||||||
setVariantString(accessible->text(QAccessible::Name), &newVal);
|
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_NamePropertyId,
|
||||||
UiaRaiseAutomationPropertyChangedEvent(provider.Get(), UIA_NamePropertyId, oldVal,
|
oldVal.get(), newVal.get());
|
||||||
newVal);
|
|
||||||
::SysFreeString(newVal.bstrVal);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,7 +417,7 @@ void QWindowsUiaMainProvider::setAriaProperties(QAccessibleInterface *accessible
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setVariantString(ariaString, pRetVal);
|
*pRetVal = QComVariant{ ariaString }.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT *pRetVal)
|
||||||
@ -449,8 +442,8 @@ void QWindowsUiaMainProvider::setStyle(QAccessibleInterface *accessible, VARIANT
|
|||||||
if (level < 1 || level > 9)
|
if (level < 1 || level > 9)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
const int styleId = styleIdForHeadingLevel(level);
|
const long styleId = styleIdForHeadingLevel(level);
|
||||||
setVariantI4(styleId, pRetVal);
|
*pRetVal = QComVariant{ styleId }.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel)
|
int QWindowsUiaMainProvider::styleIdForHeadingLevel(int headingLevel)
|
||||||
@ -490,24 +483,24 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
|||||||
switch (idProp) {
|
switch (idProp) {
|
||||||
case UIA_ProcessIdPropertyId:
|
case UIA_ProcessIdPropertyId:
|
||||||
// PID
|
// PID
|
||||||
setVariantI4(int(GetCurrentProcessId()), pRetVal);
|
*pRetVal = QComVariant{ static_cast<long>(GetCurrentProcessId()) }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_AccessKeyPropertyId:
|
case UIA_AccessKeyPropertyId:
|
||||||
// Accelerator key.
|
// Accelerator key.
|
||||||
setVariantString(accessible->text(QAccessible::Accelerator), pRetVal);
|
*pRetVal = QComVariant{ accessible->text(QAccessible::Accelerator) }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_AriaPropertiesPropertyId:
|
case UIA_AriaPropertiesPropertyId:
|
||||||
setAriaProperties(accessible, pRetVal);
|
setAriaProperties(accessible, pRetVal);
|
||||||
break;
|
break;
|
||||||
case UIA_AutomationIdPropertyId:
|
case UIA_AutomationIdPropertyId:
|
||||||
// Automation ID, which can be used by tools to select a specific control in the UI.
|
// Automation ID, which can be used by tools to select a specific control in the UI.
|
||||||
setVariantString(QAccessibleBridgeUtils::accessibleId(accessible), pRetVal);
|
*pRetVal = QComVariant{ QAccessibleBridgeUtils::accessibleId(accessible) }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_ClassNamePropertyId:
|
case UIA_ClassNamePropertyId:
|
||||||
// Class name.
|
// Class name.
|
||||||
if (QObject *o = accessible->object()) {
|
if (QObject *o = accessible->object()) {
|
||||||
QString className = QLatin1StringView(o->metaObject()->className());
|
QString className = QLatin1StringView(o->metaObject()->className());
|
||||||
setVariantString(className, pRetVal);
|
*pRetVal = QComVariant{ className }.release();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIA_DescribedByPropertyId:
|
case UIA_DescribedByPropertyId:
|
||||||
@ -520,12 +513,12 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
|||||||
fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal);
|
fillVariantArrayForRelation(accessible, QAccessible::FlowsFrom, pRetVal);
|
||||||
break;
|
break;
|
||||||
case UIA_FrameworkIdPropertyId:
|
case UIA_FrameworkIdPropertyId:
|
||||||
setVariantString(QStringLiteral("Qt"), pRetVal);
|
*pRetVal = QComVariant{ QStringLiteral("Qt") }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_ControlTypePropertyId:
|
case UIA_ControlTypePropertyId:
|
||||||
if (topLevelWindow) {
|
if (topLevelWindow) {
|
||||||
// Reports a top-level widget as a window, instead of "custom".
|
// Reports a top-level widget as a window, instead of "custom".
|
||||||
setVariantI4(UIA_WindowControlTypeId, pRetVal);
|
*pRetVal = QComVariant{ UIA_WindowControlTypeId }.release();
|
||||||
} else {
|
} else {
|
||||||
// Control type converted from role.
|
// Control type converted from role.
|
||||||
auto controlType = roleToControlTypeId(accessible->role());
|
auto controlType = roleToControlTypeId(accessible->role());
|
||||||
@ -540,63 +533,66 @@ HRESULT QWindowsUiaMainProvider::GetPropertyValue(PROPERTYID idProp, VARIANT *pR
|
|||||||
if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled))
|
if (controlType == UIA_EditControlTypeId && (!imModuleEmpty || nativeVKDisabled))
|
||||||
controlType = UIA_TextControlTypeId;
|
controlType = UIA_TextControlTypeId;
|
||||||
|
|
||||||
setVariantI4(controlType, pRetVal);
|
*pRetVal = QComVariant{ controlType }.release();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIA_HelpTextPropertyId:
|
case UIA_HelpTextPropertyId:
|
||||||
setVariantString(accessible->text(QAccessible::Help), pRetVal);
|
*pRetVal = QComVariant{ accessible->text(QAccessible::Help) }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_HasKeyboardFocusPropertyId:
|
case UIA_HasKeyboardFocusPropertyId:
|
||||||
if (topLevelWindow) {
|
if (topLevelWindow) {
|
||||||
// Windows set the active state to true when they are focused
|
// Windows set the active state to true when they are focused
|
||||||
setVariantBool(accessible->state().active, pRetVal);
|
*pRetVal = QComVariant{ accessible->state().active ? true : false }.release();
|
||||||
} else {
|
} else {
|
||||||
setVariantBool(accessible->state().focused, pRetVal);
|
*pRetVal = QComVariant{ accessible->state().focused ? true : false }.release();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIA_IsKeyboardFocusablePropertyId:
|
case UIA_IsKeyboardFocusablePropertyId:
|
||||||
if (topLevelWindow) {
|
if (topLevelWindow) {
|
||||||
// Windows should always be focusable
|
// Windows should always be focusable
|
||||||
setVariantBool(true, pRetVal);
|
*pRetVal = QComVariant{ true }.release();
|
||||||
} else {
|
} else {
|
||||||
setVariantBool(accessible->state().focusable, pRetVal);
|
*pRetVal = QComVariant{ accessible->state().focusable ? true : false }.release();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIA_IsOffscreenPropertyId:
|
case UIA_IsOffscreenPropertyId:
|
||||||
setVariantBool(accessible->state().offscreen, pRetVal);
|
*pRetVal = QComVariant{ accessible->state().offscreen ? true : false }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_IsContentElementPropertyId:
|
case UIA_IsContentElementPropertyId:
|
||||||
setVariantBool(true, pRetVal);
|
*pRetVal = QComVariant{ true }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_IsControlElementPropertyId:
|
case UIA_IsControlElementPropertyId:
|
||||||
setVariantBool(true, pRetVal);
|
*pRetVal = QComVariant{ true }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_IsEnabledPropertyId:
|
case UIA_IsEnabledPropertyId:
|
||||||
setVariantBool(!accessible->state().disabled, pRetVal);
|
*pRetVal = QComVariant{ !accessible->state().disabled }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_IsPasswordPropertyId:
|
case UIA_IsPasswordPropertyId:
|
||||||
setVariantBool(accessible->role() == QAccessible::EditableText
|
*pRetVal = QComVariant{ accessible->role() == QAccessible::EditableText
|
||||||
&& accessible->state().passwordEdit, pRetVal);
|
&& accessible->state().passwordEdit }
|
||||||
|
.release();
|
||||||
break;
|
break;
|
||||||
case UIA_IsPeripheralPropertyId:
|
case UIA_IsPeripheralPropertyId:
|
||||||
// True for peripheral UIs.
|
// True for peripheral UIs.
|
||||||
if (QWindow *window = windowForAccessible(accessible)) {
|
if (QWindow *window = windowForAccessible(accessible)) {
|
||||||
const Qt::WindowType wt = window->type();
|
const Qt::WindowType wt = window->type();
|
||||||
setVariantBool(wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen, pRetVal);
|
*pRetVal = QComVariant{ wt == Qt::Popup || wt == Qt::ToolTip || wt == Qt::SplashScreen }
|
||||||
|
.release();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case UIA_IsDialogPropertyId:
|
case UIA_IsDialogPropertyId:
|
||||||
setVariantBool(accessible->role() == QAccessible::Dialog
|
*pRetVal = QComVariant{ accessible->role() == QAccessible::Dialog
|
||||||
|| accessible->role() == QAccessible::AlertMessage, pRetVal);
|
|| accessible->role() == QAccessible::AlertMessage }
|
||||||
|
.release();
|
||||||
break;
|
break;
|
||||||
case UIA_FullDescriptionPropertyId:
|
case UIA_FullDescriptionPropertyId:
|
||||||
setVariantString(accessible->text(QAccessible::Description), pRetVal);
|
*pRetVal = QComVariant{ accessible->text(QAccessible::Description) }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_NamePropertyId: {
|
case UIA_NamePropertyId: {
|
||||||
QString name = accessible->text(QAccessible::Name);
|
QString name = accessible->text(QAccessible::Name);
|
||||||
if (name.isEmpty() && topLevelWindow)
|
if (name.isEmpty() && topLevelWindow)
|
||||||
name = QCoreApplication::applicationName();
|
name = QCoreApplication::applicationName();
|
||||||
setVariantString(name, pRetVal);
|
*pRetVal = QComVariant{ name }.release();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UIA_StyleIdAttributeId:
|
case UIA_StyleIdAttributeId:
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <QtCore/qloggingcategory.h>
|
#include <QtCore/qloggingcategory.h>
|
||||||
#include <QtCore/qstring.h>
|
#include <QtCore/qstring.h>
|
||||||
#include <QtCore/qvarlengtharray.h>
|
#include <QtCore/qvarlengtharray.h>
|
||||||
|
#include <QtCore/private/qcomvariant_p.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
@ -157,15 +158,15 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTAT
|
|||||||
|
|
||||||
switch (attributeId) {
|
switch (attributeId) {
|
||||||
case UIA_IsReadOnlyAttributeId:
|
case UIA_IsReadOnlyAttributeId:
|
||||||
setVariantBool(accessible->state().readOnly, pRetVal);
|
*pRetVal = QComVariant{ accessible->state().readOnly ? true : false }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_CaretPositionAttributeId:
|
case UIA_CaretPositionAttributeId:
|
||||||
if (textInterface->cursorPosition() == 0)
|
if (textInterface->cursorPosition() == 0)
|
||||||
setVariantI4(CaretPosition_BeginningOfLine, pRetVal);
|
*pRetVal = QComVariant{ static_cast<long>(CaretPosition_BeginningOfLine) }.release();
|
||||||
else if (textInterface->cursorPosition() == textInterface->characterCount())
|
else if (textInterface->cursorPosition() == textInterface->characterCount())
|
||||||
setVariantI4(CaretPosition_EndOfLine, pRetVal);
|
*pRetVal = QComVariant{ static_cast<long>(CaretPosition_EndOfLine) }.release();
|
||||||
else
|
else
|
||||||
setVariantI4(CaretPosition_Unknown, pRetVal);
|
*pRetVal = QComVariant{ static_cast<long>(CaretPosition_Unknown) }.release();
|
||||||
break;
|
break;
|
||||||
case UIA_StrikethroughStyleAttributeId:
|
case UIA_StrikethroughStyleAttributeId:
|
||||||
{
|
{
|
||||||
@ -173,7 +174,7 @@ HRESULT STDMETHODCALLTYPE QWindowsUiaTextRangeProvider::GetAttributeValue(TEXTAT
|
|||||||
if (value.isEmpty())
|
if (value.isEmpty())
|
||||||
break;
|
break;
|
||||||
const TextDecorationLineStyle uiaLineStyle = uiaLineStyleForIA2LineStyle(value);
|
const TextDecorationLineStyle uiaLineStyle = uiaLineStyleForIA2LineStyle(value);
|
||||||
setVariantI4(uiaLineStyle, pRetVal);
|
*pRetVal = QComVariant{ static_cast<long>(uiaLineStyle) }.release();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
@ -63,30 +63,6 @@ void clearVariant(VARIANT *variant)
|
|||||||
variant->punkVal = nullptr;
|
variant->punkVal = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVariantI4(int value, VARIANT *variant)
|
|
||||||
{
|
|
||||||
variant->vt = VT_I4;
|
|
||||||
variant->lVal = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setVariantBool(bool value, VARIANT *variant)
|
|
||||||
{
|
|
||||||
variant->vt = VT_BOOL;
|
|
||||||
variant->boolVal = value ? -1 : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setVariantDouble(double value, VARIANT *variant)
|
|
||||||
{
|
|
||||||
variant->vt = VT_R8;
|
|
||||||
variant->dblVal = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setVariantString(const QString &value, VARIANT *variant)
|
|
||||||
{
|
|
||||||
variant->vt = VT_BSTR;
|
|
||||||
variant->bstrVal = QBStr(value).release();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scales a rect to native coordinates, according to high dpi settings.
|
// Scales a rect to native coordinates, according to high dpi settings.
|
||||||
void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect)
|
void rectToNativeUiaRect(const QRect &rect, const QWindow *w, UiaRect *uiaRect)
|
||||||
{
|
{
|
||||||
|
@ -33,14 +33,6 @@ bool isTextUnitSeparator(TextUnit unit, const QChar &ch);
|
|||||||
|
|
||||||
void clearVariant(VARIANT *variant);
|
void clearVariant(VARIANT *variant);
|
||||||
|
|
||||||
void setVariantI4(int value, VARIANT *variant);
|
|
||||||
|
|
||||||
void setVariantBool(bool value, VARIANT *variant);
|
|
||||||
|
|
||||||
void setVariantDouble(double value, VARIANT *variant);
|
|
||||||
|
|
||||||
void setVariantString(const QString &value, VARIANT *variant);
|
|
||||||
|
|
||||||
} // namespace QWindowsUiAutomation
|
} // namespace QWindowsUiAutomation
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -3,3 +3,4 @@
|
|||||||
|
|
||||||
add_subdirectory(qcomobject)
|
add_subdirectory(qcomobject)
|
||||||
add_subdirectory(qbstr)
|
add_subdirectory(qbstr)
|
||||||
|
add_subdirectory(qcomvariant)
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
# Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
# SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
|
||||||
|
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
project(tst_qcomobject LANGUAGES CXX)
|
||||||
|
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
qt_internal_add_test(tst_qcomvariant
|
||||||
|
SOURCES
|
||||||
|
tst_qcomvariant.cpp
|
||||||
|
LIBRARIES
|
||||||
|
Qt::CorePrivate
|
||||||
|
)
|
@ -0,0 +1,199 @@
|
|||||||
|
// Copyright (C) 2024 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <private/qcomvariant_p.h>
|
||||||
|
#include <private/qcomobject_p.h>
|
||||||
|
#include <wrl/client.h>
|
||||||
|
|
||||||
|
using Microsoft::WRL::ComPtr;
|
||||||
|
|
||||||
|
QT_USE_NAMESPACE
|
||||||
|
|
||||||
|
using namespace Qt::StringLiterals;
|
||||||
|
|
||||||
|
struct IUnknownStub : QComObject<IUnknown>
|
||||||
|
{
|
||||||
|
ULONG refCount()
|
||||||
|
{
|
||||||
|
AddRef();
|
||||||
|
return Release();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
void __cdecl SetOaNoCache(void);
|
||||||
|
}
|
||||||
|
|
||||||
|
class tst_qcomvariant : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void initTestCase()
|
||||||
|
{
|
||||||
|
SetOaNoCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
static void constructor_initializesToEmpty()
|
||||||
|
{
|
||||||
|
const QComVariant var;
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withBool()
|
||||||
|
{
|
||||||
|
QComVariant falseVal{ false };
|
||||||
|
QCOMPARE_EQ(falseVal.get().vt, VT_BOOL);
|
||||||
|
QCOMPARE_EQ(falseVal.get().boolVal, VARIANT_FALSE);
|
||||||
|
|
||||||
|
QComVariant trueVal{ true };
|
||||||
|
QCOMPARE_EQ(trueVal.get().vt, VT_BOOL);
|
||||||
|
QCOMPARE_EQ(trueVal.get().boolVal, VARIANT_TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withInt()
|
||||||
|
{
|
||||||
|
QComVariant var{ 3 };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_INT);
|
||||||
|
QCOMPARE_EQ(var.get().intVal, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withLong()
|
||||||
|
{
|
||||||
|
QComVariant var{ 4l };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_I4);
|
||||||
|
QCOMPARE_EQ(var.get().lVal, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withDouble()
|
||||||
|
{
|
||||||
|
QComVariant var{ 3.14 };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_R8);
|
||||||
|
QVERIFY(qFuzzyCompare(var.get().dblVal, 3.14));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withQBStr()
|
||||||
|
{
|
||||||
|
QComVariant var{ QBStr{ L"Hello world!" } };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||||
|
QCOMPARE_EQ(var.get().bstrVal, QStringView(L"Hello world!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializesWithNullQBStr()
|
||||||
|
{
|
||||||
|
QComVariant var{ QBStr{ nullptr } };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||||
|
QCOMPARE_EQ(var.get().bstrVal, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withQString()
|
||||||
|
{
|
||||||
|
QComVariant var{ "Hello world!"_L1 };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||||
|
QCOMPARE_EQ(var.get().bstrVal, QStringView(L"Hello world!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializesWithNullQString()
|
||||||
|
{
|
||||||
|
QComVariant var{ QString{} };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||||
|
QCOMPARE_EQ(var.get().bstrVal, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializesWithEmptyQString()
|
||||||
|
{
|
||||||
|
QComVariant var{ QString{ ""_L1 } };
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_BSTR);
|
||||||
|
QCOMPARE_EQ(var.get().bstrVal, ""_L1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withIUnknown()
|
||||||
|
{
|
||||||
|
const ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||||
|
|
||||||
|
QComVariant var{ value };
|
||||||
|
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_UNKNOWN);
|
||||||
|
QCOMPARE_EQ(var.get().punkVal, value.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constructor_initializes_withNullptrIUnknown()
|
||||||
|
{
|
||||||
|
ComPtr<IUnknown> ptr;
|
||||||
|
QComVariant var{ ptr };
|
||||||
|
|
||||||
|
QCOMPARE_EQ(var.get().vt, VT_UNKNOWN);
|
||||||
|
QCOMPARE_EQ(var.get().punkVal, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addressOfOperator_clearsVariant_whenVariantOwnsIUnknown()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||||
|
|
||||||
|
QComVariant var{ value };
|
||||||
|
QCOMPARE_EQ(value->refCount(), 2);
|
||||||
|
|
||||||
|
// Act
|
||||||
|
const VARIANT *ptr = &var;
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
QCOMPARE_EQ(value->refCount(), 1);
|
||||||
|
QCOMPARE_EQ(ptr->vt, VT_EMPTY);
|
||||||
|
QCOMPARE_EQ(ptr->punkVal, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destructor_clearsVariant_whenOwningIUnknown()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||||
|
|
||||||
|
{
|
||||||
|
QComVariant var{ value };
|
||||||
|
QCOMPARE_EQ(value->refCount(), 2);
|
||||||
|
// Act (run destructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
QCOMPARE_EQ(value->refCount(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void destructor_doesNotCrash_whenOwningBSTR()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
QComVariant var{ QBStr{ L"Hello world!" } };
|
||||||
|
|
||||||
|
// Act (run destructor)
|
||||||
|
}
|
||||||
|
|
||||||
|
static void release_releasesOwnershipAndReturnsVariant_whenOwningIUnknown()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
const ComPtr<IUnknownStub> value = makeComObject<IUnknownStub>();
|
||||||
|
|
||||||
|
VARIANT detached{};
|
||||||
|
{
|
||||||
|
QComVariant var{ value };
|
||||||
|
QCOMPARE_EQ(value->refCount(), 2);
|
||||||
|
|
||||||
|
// Act (Release and run destructor)
|
||||||
|
detached = var.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
QCOMPARE_EQ(value->refCount(), 2);
|
||||||
|
QCOMPARE_EQ(detached.vt, VT_UNKNOWN);
|
||||||
|
QCOMPARE_EQ(detached.punkVal, value.Get());
|
||||||
|
|
||||||
|
// Cleanup
|
||||||
|
QCOMPARE_EQ(VariantClear(&detached), S_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
QTEST_MAIN(tst_qcomvariant)
|
||||||
|
#include "tst_qcomvariant.moc"
|
Loading…
x
Reference in New Issue
Block a user