1 | // Copyright (C) 2024 The Qt Company Ltd. |
---|---|
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #include "qquickfluentwinui3focusframe_p.h" |
5 | |
6 | #include <private/qquickitem_p.h> |
7 | |
8 | #include <QtCore/qmetaobject.h> |
9 | |
10 | #include <QtGui/qguiapplication.h> |
11 | |
12 | #include <QtQml/qqmlengine.h> |
13 | #include <QtQml/qqmlcontext.h> |
14 | #include <QtQml/qqmlcomponent.h> |
15 | |
16 | |
17 | QT_BEGIN_NAMESPACE |
18 | |
19 | QScopedPointer<QQuickItem> QQuickFluentWinUI3FocusFrame::m_focusFrame; |
20 | |
21 | QQuickFluentWinUI3FocusFrame::QQuickFluentWinUI3FocusFrame() |
22 | { |
23 | connect(qGuiApp, signal: &QGuiApplication::focusObjectChanged, context: this, slot: [this](QObject *focusObject){ |
24 | if (QQuickControl *control = qobject_cast<QQuickControl *>(object: focusObject); |
25 | control && (control->focusReason() == Qt::FocusReason::TabFocusReason |
26 | || control->focusReason() == Qt::FocusReason::BacktabFocusReason |
27 | || control->focusReason() == Qt::FocusReason::OtherFocusReason)) { |
28 | moveToItem(item: control); |
29 | } else { |
30 | moveToItem(item: nullptr); |
31 | } |
32 | }); |
33 | } |
34 | |
35 | QQuickItem *QQuickFluentWinUI3FocusFrame::createFocusFrame(QQmlContext *context) |
36 | { |
37 | QQmlComponent component( |
38 | context->engine(), |
39 | QUrl(QStringLiteral( |
40 | "qrc:/qt-project.org/imports/QtQuick/Controls/FluentWinUI3/FocusFrame.qml"))); |
41 | auto frame = qobject_cast<QQuickItem *>(o: component.create()); |
42 | if (!frame) |
43 | return nullptr; |
44 | return frame; |
45 | } |
46 | |
47 | void QQuickFluentWinUI3FocusFrame::moveToItem(QQuickControl *item) |
48 | { |
49 | if (!m_focusFrame) { |
50 | const auto context = QQmlEngine::contextForObject(item); |
51 | // In certain cases like QQuickWebEngineView, the item |
52 | // gets focus even though it has no QQmlEngine associated with its context. |
53 | // We need the engine for creating the focus frame component. |
54 | if (!context || !context->engine()) |
55 | return; |
56 | m_focusFrame.reset(other: createFocusFrame(context)); |
57 | QQuickItemPrivate::get(item: m_focusFrame.get())->setTransparentForPositioner(true); |
58 | if (!m_focusFrame) { |
59 | qWarning() << "Failed to create FocusFrame"; |
60 | return; |
61 | } |
62 | } |
63 | |
64 | const auto target = getFocusTarget(focusItem: item); |
65 | QMetaObject::invokeMethod(obj: m_focusFrame.data(), member: "moveToItem", |
66 | Q_ARG(QVariant, QVariant::fromValue(target))); |
67 | } |
68 | |
69 | QQuickControl *QQuickFluentWinUI3FocusFrame::getFocusTarget(QQuickControl *focusItem) const |
70 | { |
71 | if (!focusItem) |
72 | return nullptr; |
73 | |
74 | const auto parentItem = focusItem->parentItem(); |
75 | if (!parentItem) |
76 | return nullptr; |
77 | |
78 | // The control that gets active focus can be a child of the control (e.g |
79 | // editable ComboBox). In that case, resolve the actual control first. |
80 | const auto proxy = focusItem->property(name: "__focusFrameControl").value<QQuickControl *>(); |
81 | const auto control = proxy ? proxy : focusItem; |
82 | auto target = control->property(name: "__focusFrameTarget").value<QQuickControl *>(); |
83 | |
84 | return target; |
85 | } |
86 | |
87 | QT_END_NAMESPACE |
88 |