diff options
Diffstat (limited to 'src/imports/nativestyle')
-rw-r--r-- | src/imports/nativestyle/CMakeLists.txt | 16 | ||||
-rw-r--r-- | src/imports/nativestyle/nativestyle.pro | 1 | ||||
-rw-r--r-- | src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp | 9 | ||||
-rw-r--r-- | src/imports/nativestyle/util/FocusFrame.qml | 118 | ||||
-rw-r--r-- | src/imports/nativestyle/util/qquickmacfocusframe.h | 73 | ||||
-rw-r--r-- | src/imports/nativestyle/util/qquickmacfocusframe.mm | 179 | ||||
-rw-r--r-- | src/imports/nativestyle/util/util.pri | 11 |
7 files changed, 407 insertions, 0 deletions
diff --git a/src/imports/nativestyle/CMakeLists.txt b/src/imports/nativestyle/CMakeLists.txt index 1b6ce728..d6021e04 100644 --- a/src/imports/nativestyle/CMakeLists.txt +++ b/src/imports/nativestyle/CMakeLists.txt @@ -39,6 +39,7 @@ qt_add_qml_module(qtquickcontrols2nativestyleplugin INCLUDE_DIRECTORIES items qstyle + util LIBRARIES Qt::CorePrivate Qt::GuiPrivate @@ -69,12 +70,27 @@ qt_extend_target(qtquickcontrols2nativestyleplugin CONDITION MACOS SOURCES qstyle/mac/qquickmacstyle_mac.mm qstyle/mac/qquickmacstyle_mac_p.h qstyle/mac/qquickmacstyle_mac_p_p.h + util/qquickmacfocusframe.h util/qquickmacfocusframe.mm INCLUDE_DIRECTORIES qstyle/mac LIBRARIES ${FWAppKit} ) +if(MACOS) + # Resources: + set(qmake_immediate_resource_files + "util/FocusFrame.qml" + ) + + qt_add_resource(qtquickcontrols2nativestyleplugin "qmake_immediate" + PREFIX + "/" + FILES + ${qmake_immediate_resource_files} + ) +endif() + qt_extend_target(qtquickcontrols2nativestyleplugin CONDITION WIN32 SOURCES qstyle/windows/qquickwindowsstyle.cpp qstyle/windows/qquickwindowsstyle_p.h diff --git a/src/imports/nativestyle/nativestyle.pro b/src/imports/nativestyle/nativestyle.pro index b06e5daf..c493080a 100644 --- a/src/imports/nativestyle/nativestyle.pro +++ b/src/imports/nativestyle/nativestyle.pro @@ -12,6 +12,7 @@ DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII include(items/items.pri) include(qstyle/qstyle.pri) include(controls/controls.pri) +include(util/util.pri) OTHER_FILES += \ qmldir \ diff --git a/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp b/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp index ef8bfb9f..cf9b5ccf 100644 --- a/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp +++ b/src/imports/nativestyle/qtquickcontrols2nativestyleplugin.cpp @@ -45,6 +45,7 @@ #if defined(Q_OS_MACOS) #include "qquickmacstyle_mac_p.h" +#include "qquickmacfocusframe.h" #elif defined(Q_OS_WINDOWS) # include "qquickwindowsxpstyle_p.h" #endif @@ -64,6 +65,10 @@ public: void initializeEngine(QQmlEngine *engine, const char *uri) override; void initializeTheme(QQuickTheme *theme) override; QString name() const override; + +#if defined(Q_OS_MACOS) + QScopedPointer<QQuickMacFocusFrame> m_focusFrame; +#endif }; static void deleteQStyle() @@ -132,6 +137,10 @@ void QtQuickControls2NativeStylePlugin::initializeEngine(QQmlEngine *engine, con } } +#if defined(Q_OS_MACOS) + m_focusFrame.reset(new QQuickMacFocusFrame()); +#endif + qAddPostRoutine(deleteQStyle); QQuickNativeStyle::setStyle(style); } diff --git a/src/imports/nativestyle/util/FocusFrame.qml b/src/imports/nativestyle/util/FocusFrame.qml new file mode 100644 index 00000000..6bac460d --- /dev/null +++ b/src/imports/nativestyle/util/FocusFrame.qml @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: root + + // It's important that this item has a zero size. Otherwise, if the parent of the + // targetItem is e.g a layout, we will change the layout if we parent this item inside it. + width: 0 + height: 0 + // Stack on top of all siblings of the targetItem + z: 100 + + function moveToItem(item, margins, radius) { + parent = item.parent + targetItem = item + leftOffset = margins.left + rightOffset = margins.right + topOffset = margins.top + bottomOffset = margins.bottom + frameRadius = radius + animation.restart() + } + + property Item targetItem + property real leftOffset: 0 + property real rightOffset: 0 + property real topOffset: 0 + property real bottomOffset: 0 + property real frameOpacity: 0 + property real frameSize: 0 + property real frameRadius: 0 + + property point targetItemPos: { + if (!targetItem) + return Qt.point(0, 0) + // Force a reevaluation if + // the target item moves + targetItem.x + targetItem.y + mapFromItem(targetItem, Qt.point(0, 0)) + } + + // systemFrameColor is set to NSColor.keyboardFocusIndicatorColor from cpp + property color systemFrameColor + + Rectangle { + id: focusFrame + z: 10 + x: targetItemPos.x + leftOffset - frameSize + y: targetItemPos.y + topOffset - frameSize + width: targetItem ? targetItem.width - leftOffset - rightOffset + (frameSize * 2) : 0 + height: targetItem ? targetItem.height - topOffset - bottomOffset + (frameSize * 2) : 0 + radius: frameRadius + visible: targetItem + color: "transparent" + + border.color: systemFrameColor + border.width: frameSize + } + + ParallelAnimation { + id: animation + NumberAnimation { + target: root + property: "frameSize" + duration: 300 + from: 15 + to: 2.5 + easing.type: Easing.OutCubic + } + NumberAnimation { + target: focusFrame + property: "opacity" + duration: 300 + from: 0 + to: 0.55 + easing.type: Easing.OutCubic + } + } +} diff --git a/src/imports/nativestyle/util/qquickmacfocusframe.h b/src/imports/nativestyle/util/qquickmacfocusframe.h new file mode 100644 index 00000000..425b2a68 --- /dev/null +++ b/src/imports/nativestyle/util/qquickmacfocusframe.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQUICKMACFOCUSFRAME_H +#define QQUICKMACFOCUSFRAME_H + +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquicktextedit_p.h> +#include "qquickstyleitem.h" + +QT_BEGIN_NAMESPACE + +Q_DECLARE_LOGGING_CATEGORY(lcFocusFrame) + +struct QQuickFocusFrameDescription { + QQuickItem *target; + QQuickStyleMargins margins; + const qreal radius = 3; + bool isValid() const { return target != nullptr; } + static QQuickFocusFrameDescription Invalid; +}; + +class QQuickMacFocusFrame : public QObject +{ + Q_OBJECT + +public: + QQuickMacFocusFrame(); + +private: + static QScopedPointer<QQuickItem> m_focusFrame; + + void createFocusFrame(QQmlContext *context); + void moveToItem(QQuickItem *item); + QQuickFocusFrameDescription getDescriptionForItem(QQuickItem *focusItem) const; +}; + +QT_END_NAMESPACE + +#endif // QQUICKMACFOCUSFRAME_H diff --git a/src/imports/nativestyle/util/qquickmacfocusframe.mm b/src/imports/nativestyle/util/qquickmacfocusframe.mm new file mode 100644 index 00000000..87d37366 --- /dev/null +++ b/src/imports/nativestyle/util/qquickmacfocusframe.mm @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing/ +** +** This file is part of the Qt Quick Controls 2 module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL3$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see http://www.qt.io/terms-conditions. For further +** information use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPLv3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or later as published by the Free +** Software Foundation and appearing in the file LICENSE.GPL included in +** the packaging of this file. Please review the following information to +** ensure the GNU General Public License version 2.0 requirements will be +** met: http://www.gnu.org/licenses/gpl-2.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qquickmacfocusframe.h" + +#include <AppKit/AppKit.h> + +#include <QtCore/qmetaobject.h> + +#include <QtGui/qguiapplication.h> +#include <QtGui/private/qcoregraphics_p.h> + +#include <QtQml/qqmlengine.h> +#include <QtQml/qqmlcontext.h> +#include <QtQml/qqmlcomponent.h> + +#include <QtQuick/qquickitem.h> +#include <QtQuick/private/qquicktextinput_p.h> +#include <QtQuick/private/qquicktextedit_p.h> +#include <QtQuick/private/qquickflickable_p.h> + +#include <QtQuickTemplates2/private/qquickframe_p.h> +#include <QtQuickTemplates2/private/qquickbutton_p.h> +#include <QtQuickTemplates2/private/qquickscrollview_p.h> +#include <QtQuickTemplates2/private/qquickslider_p.h> +#include <QtQuickTemplates2/private/qquickcombobox_p.h> +#include <QtQuickTemplates2/private/qquickcheckbox_p.h> +#include <QtQuickTemplates2/private/qquickradiobutton_p.h> +#include <QtQuickTemplates2/private/qquickspinbox_p.h> +#include <QtQuickTemplates2/private/qquicktextfield_p.h> +#include <QtQuickTemplates2/private/qquicktextarea_p.h> + +#include "items/qquickstyleitem.h" +#include "qquicknativestyle.h" + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcFocusFrame, "qt.quick.controls.focusframe") + +QQuickFocusFrameDescription QQuickFocusFrameDescription::Invalid = { nullptr, QQuickStyleMargins(), 0 }; +QScopedPointer<QQuickItem> QQuickMacFocusFrame::m_focusFrame; + +QQuickMacFocusFrame::QQuickMacFocusFrame() +{ + connect(qGuiApp, &QGuiApplication::focusObjectChanged, [=]{ + if (auto item = qobject_cast<QQuickItem *>(qGuiApp->focusObject())) + moveToItem(item); + }); +} + +void QQuickMacFocusFrame::moveToItem(QQuickItem *item) +{ + if (!m_focusFrame) { + const auto context = QQmlEngine::contextForObject(item); + if (!context) + return; + createFocusFrame(context); + } + + const QQuickFocusFrameDescription &config = getDescriptionForItem(item); + if (!config.isValid()) { + m_focusFrame->setParentItem(nullptr); + m_focusFrame->setVisible(false); + return; + } + + m_focusFrame->setVisible(true); + QMetaObject::invokeMethod(m_focusFrame.data(), "moveToItem", + Q_ARG(QVariant, QVariant::fromValue(config.target)), + Q_ARG(QVariant, QVariant::fromValue(config.margins)), + Q_ARG(QVariant, QVariant::fromValue(config.radius))); +} + +void QQuickMacFocusFrame::createFocusFrame(QQmlContext *context) +{ + QQmlComponent component(context->engine(), QUrl(QStringLiteral("qrc:/util/FocusFrame.qml"))); + m_focusFrame.reset(qobject_cast<QQuickItem *>(component.create())); + + auto indicatorColor = qt_mac_toQColor(NSColor.keyboardFocusIndicatorColor.CGColor); + indicatorColor.setAlpha(255); + m_focusFrame->setProperty("systemFrameColor", indicatorColor); +} + +QQuickFocusFrameDescription QQuickMacFocusFrame::getDescriptionForItem(QQuickItem *focusItem) const +{ + qCDebug(lcFocusFrame) << "new focusobject:" << focusItem; + const auto parentItem = focusItem->parentItem(); + if (!parentItem) + return QQuickFocusFrameDescription::Invalid; + + // The item that gets active focus can be a child of the control (e.g + // editable ComboBox). In that case, resolve the actual control first. + const auto proxy = focusItem->property("__focusFrameControl").value<QQuickItem *>(); + const auto control = proxy ? proxy : focusItem; + const auto target = control->property("__focusFrameTarget").value<QQuickItem *>(); + qCDebug(lcFocusFrame) << "target:" << target; + qCDebug(lcFocusFrame) << "control:" << control; + + if (!target) { + // __focusFrameTarget points to the item in the control that should + // get the focus frame. This is usually the control itself, but can + // sometimes be a child (CheckBox), and other times a grand parent + // (a ScrollView that has a TextArea as child). We anyway require + // this property to be set if we are to show the focus frame around + // the control in the first place. So for controls that don't want + // a frame (ProgressBar), we simply skip setting it. + // Also, we should never show a focus frame around custom controls. + // None of the built-in styles do that, so to be consistent, we + // shouldn't either. Besides, drawing a focus frame around an unknown + // item without any way to turn it off can easily be unwanted. A better + // way for custom controls to get a native focus frame is for us to offer + // a FocusFrame control (QTBUG-86818). + return QQuickFocusFrameDescription::Invalid; + } + + // If the control gives us a QQuickStyleItem, we use that to configure the focus frame. + // By default we assume that the background delegate is a QQuickStyleItem, but the + // control can override this by setting __focusFrameStyleItem. + const auto styleItemProperty = control->property("__focusFrameStyleItem"); + auto item = styleItemProperty.value<QQuickItem *>(); + if (!item) { + const auto styleItemProperty = control->property("background"); + item = styleItemProperty.value<QQuickItem *>(); + } + qCDebug(lcFocusFrame) << "styleItem:" << item; + if (!item) + return QQuickFocusFrameDescription::Invalid; + if (QQuickStyleItem *styleItem = qobject_cast<QQuickStyleItem *>(item)) + return { target, QQuickStyleMargins(styleItem->layoutMargins()), styleItem->focusFrameRadius() }; + + // Some controls don't have a QQuickStyleItem (TextArea). But if the __focusFrameStyleItem + // has a "__isDefaultDelegate" property set, we show a default focus frame instead. + if (item->property("__isDefaultDelegate").toBool() == true) { + qCDebug(lcFocusFrame) << "'__isDefaultDelegate' property found, showing a default focus frame"; + const QStyleOption opt; + const qreal radius = QQuickNativeStyle::style()->pixelMetric(QStyle::PM_TextFieldFocusFrameRadius, &opt); + return { target, QQuickStyleMargins(), radius }; + } + + // The application has set a custom delegate on the control. In that + // case, it's the delegates responsibility to draw a focus frame. + qCDebug(lcFocusFrame) << "custom delegates in use, skip showing focus frame"; + return QQuickFocusFrameDescription::Invalid; +} + +QT_END_NAMESPACE diff --git a/src/imports/nativestyle/util/util.pri b/src/imports/nativestyle/util/util.pri new file mode 100644 index 00000000..96786eac --- /dev/null +++ b/src/imports/nativestyle/util/util.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $$PWD + +macos { + HEADERS += \ + $$PWD/qquickmacfocusframe.h \ + + SOURCES += \ + $$PWD/qquickmacfocusframe.mm \ + + RESOURCES += $$PWD/FocusFrame.qml +} |