diff --git a/examples/galleryapp/resources.qrc b/examples/galleryapp/resources.qrc --- a/examples/galleryapp/resources.qrc +++ b/examples/galleryapp/resources.qrc @@ -18,6 +18,7 @@ ../gallerydata/contents/ui/gallery/ColorsGallery.qml ../gallerydata/contents/ui/gallery/MetricsGallery.qml ../gallerydata/contents/ui/gallery/LayersGallery.qml + ../gallerydata/contents/ui/gallery/FormLayoutGallery.qml ../gallerydata/contents/ui/ExampleApp.qml ../gallerydata/contents/ui/gallery/ColorSetGallery.qml ../gallerydata/contents/ui/DesktopExampleApp.qml diff --git a/examples/gallerydata/contents/ui/MainPage.qml b/examples/gallerydata/contents/ui/MainPage.qml --- a/examples/gallerydata/contents/ui/MainPage.qml +++ b/examples/gallerydata/contents/ui/MainPage.qml @@ -99,6 +99,10 @@ component: "TextField" } ListElement { + text: "Form Layout" + component: "FormLayout" + } + ListElement { text: "Multiple Columns" component: "MultipleColumns" } diff --git a/examples/gallerydata/contents/ui/gallery/FormLayoutGallery.qml b/examples/gallerydata/contents/ui/gallery/FormLayoutGallery.qml new file mode 100644 --- /dev/null +++ b/examples/gallerydata/contents/ui/gallery/FormLayoutGallery.qml @@ -0,0 +1,95 @@ + + +import QtQuick 2.6 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.3 as Kirigami + +Kirigami.ScrollablePage { + title: "Form Layout" + + Kirigami.FormLayout { + id: layout + width: 500 + height: 500 + TextField { + Kirigami.FormData.label: "Label:" + } + TextField { + } + TextField { + Kirigami.FormData.label:"Lo&nger label:" + } + Kirigami.Separator { + Kirigami.FormData.isSection: true + } + TextField { + Kirigami.FormData.label: "After separator:" + } + ComboBox { + Kirigami.FormData.label: "Combo:" + model: ["First", "Second", "Third"] + } + CheckBox { + checked: true + text: "Option" + } + Kirigami.Separator { + Kirigami.FormData.isSection: true + Kirigami.FormData.label: "Section title" + } + TextField { + Kirigami.FormData.label: "Label:" + } + Item { + width:1 + height:1 + Kirigami.FormData.isSection: true + } + TextField { + Kirigami.FormData.label: "Section without line:" + } + TextField { + } + Item { + width:1 + height:1 + Kirigami.FormData.isSection: true + Kirigami.FormData.label: "Section with title without line" + } + TextField { + Kirigami.FormData.label: "Title:" + } + ColumnLayout { + Layout.rowSpan: 3 + Kirigami.FormData.label: "Label for radios:" + RadioButton { + checked: true + text: "One" + } + RadioButton { + text: "Two" + } + RadioButton { + text: "Three" + } + } + Button { + text: item ? "Remove Field" : "Add Field" + property var item + onClicked: { + if (item) { + item.destroy(); + } else { + item = dyncomponent.createObject(layout); + } + } + Component { + id: dyncomponent + TextField { + Kirigami.FormData.label: "Generated Title:" + } + } + } + } +} diff --git a/examples/gallerydata/contents/ui/gallery/TextFieldGallery.qml b/examples/gallerydata/contents/ui/gallery/TextFieldGallery.qml --- a/examples/gallerydata/contents/ui/gallery/TextFieldGallery.qml +++ b/examples/gallerydata/contents/ui/gallery/TextFieldGallery.qml @@ -20,49 +20,43 @@ import QtQuick 2.0 import QtQuick.Controls 2.0 as Controls import QtQuick.Layouts 1.2 -import org.kde.kirigami 2.2 +import org.kde.kirigami 2.3 as Kirigami -ScrollablePage { +Kirigami.ScrollablePage { id: page Layout.fillWidth: true implicitWidth: applicationWindow().width title: "Text fields" ColumnLayout { - objectName: "pollo" - width: page.width - spacing: Units.smallSpacing - - Controls.Label { - text: "Placeholder text:" - } - Controls.TextField { - placeholderText: "Search..." - Layout.alignment: Qt.AlignHCenter - } - Controls.Label { - text: "Disabled field:" - } - Controls.TextField { - text: "Disabled" - enabled: false - Layout.alignment: Qt.AlignHCenter - } - Controls.Label { - text: "Password:" - } - Controls.TextField { - echoMode: TextInput.Password - Layout.alignment: Qt.AlignHCenter - } - Controls.Label { - text: "Numbers:" - } - Controls.TextField { - inputMask: "99999999" - inputMethodHints: Qt.ImhDigitsOnly + Kirigami.FormLayout { Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + + width: page.width + spacing: Units.smallSpacing + + Controls.TextField { + placeholderText: "Search..." + Kirigami.FormData.label: "Placeholder text:" + } + Controls.TextField { + text: "Disabled" + enabled: false + Kirigami.FormData.label: "Disabled field:" + } + Controls.TextField { + echoMode: TextInput.Password + Kirigami.FormData.label: "Password:" + } + + Controls.TextField { + inputMask: "99999999" + inputMethodHints: Qt.ImhDigitsOnly + Kirigami.FormData.label: "Numbers:" + } } + Controls.Label { text: "Text area:" } @@ -81,8 +75,8 @@ id: field anchors.fill: parent text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eu nisl ac nibh malesuada pretium ut sit amet libero. Nulla libero arcu, pharetra a dignissim nec, iaculis sit amet metus. Suspendisse quis justo efficitur, pharetra dui maximus, aliquam dolor. Vestibulum vel imperdiet turpis. Mauris ut leo mauris. Praesent ut libero sollicitudin, tincidunt nisi a, efficitur erat. Curabitur lacinia leo et tempor aliquam." - Layout.minimumWidth: Units.gridUnit * 12 - Layout.minimumHeight: Units.gridUnit * 12 + Layout.minimumWidth: Kirigami.Units.gridUnit * 12 + Layout.minimumHeight: Kirigami.Units.gridUnit * 12 wrapMode: Controls.TextArea.WordWrap } } diff --git a/kirigami.qrc b/kirigami.qrc --- a/kirigami.qrc +++ b/kirigami.qrc @@ -44,6 +44,7 @@ src/controls/Label.qml src/controls/BasicListItem.qml src/controls/AbstractApplicationHeader.qml + src/controls/FormLayout.qml src/styles/Plasma/Theme.qml src/styles/Plasma/Units.qml src/styles/Plasma/Icon.qml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -16,6 +16,8 @@ enums.cpp desktopicon.cpp settings.cpp + formlayoutattached.cpp + mnemonicattached.cpp ${kirigami_QM_LOADER} ${KIRIGAMI_STATIC_FILES} ) diff --git a/src/controls/FormLayout.qml b/src/controls/FormLayout.qml new file mode 100644 --- /dev/null +++ b/src/controls/FormLayout.qml @@ -0,0 +1,27 @@ +/* + * Copyright 2017 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.6 +import org.kde.kirigami 2.3 as Kirigami + +import "templates" as T + +T.FormLayout { + +} diff --git a/src/controls/Heading.qml b/src/controls/Heading.qml --- a/src/controls/Heading.qml +++ b/src/controls/Heading.qml @@ -65,7 +65,7 @@ lineHeight: 1.2 font.pointSize: headerPointSize(level) - font.weight: Font.Light + font.weight: level <= 4 ? Font.Light : Font.Normal wrapMode: Text.WordWrap opacity: 0.8 diff --git a/src/controls/private/PrivateActionToolButton.qml b/src/controls/private/PrivateActionToolButton.qml --- a/src/controls/private/PrivateActionToolButton.qml +++ b/src/controls/private/PrivateActionToolButton.qml @@ -60,7 +60,7 @@ source: control.action ? control.action.iconName : "" visible: control.action && control.action.iconName != "" } - Label { + Controls.Label { text: action ? action.text : "" visible: control.showText } diff --git a/src/controls/templates/FormLayout.qml b/src/controls/templates/FormLayout.qml new file mode 100644 --- /dev/null +++ b/src/controls/templates/FormLayout.qml @@ -0,0 +1,176 @@ +/* + * Copyright 2017 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2 or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Library General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +import QtQuick 2.6 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.2 +import org.kde.kirigami 2.3 as Kirigami + +Control { + id: root + implicitWidth: lay.implicitWidth + implicitHeight: lay.implicitHeight + Layout.preferredHeight: lay.implicitHeight + property bool wideMode: width >= lay.wideImplicitWidth + + GridLayout { + id: lay + property int wideImplicitWidth + columns: root.wideMode ? 2 : 1 + rowSpacing: Kirigami.Units.smallSpacing + columnSpacing: Kirigami.Units.smallSpacing + property var knownItems: [] + anchors { + left: parent.left + top: parent.top + right: parent.right + } + + Timer { + id: hintCompression + onTriggered: { + if (root.wideMode) { + lay.wideImplicitWidth = lay.implicitWidth; + } + } + } + onImplicitWidthChanged: hintCompression.restart(); + Component.onCompleted: wideImplicitWidth = lay.implicitWidth; + } + + Item { + id: temp + } + + Timer { + id: relayoutTimer + interval: 0 + onTriggered: { + var __items = children; + //exclude the layout and temp + for (var i = 2; i < __items.length; ++i) { + var item = __items[i]; + + //skip items that are already there + if (lay.knownItems.indexOf(item) != -1 || + //exclude Repeaters + //NOTE: this is an heuristic but there are't better ways + (item.model !== undefined && item.children.length == 0)) { + continue; + } + lay.knownItems.push(item); + + var itemContainer = itemComponent.createObject(temp, {"item": item}) + + //if section, label goes after the separator + if (item.Kirigami.FormData.isSection) { + //put an extra spacer + var placeHolder = placeHolderComponent.createObject(lay, {"item": item}); + placeHolder.Layout.colSpan = 2; + itemContainer.parent = lay; + } + + var buddy = buddyComponent.createObject(lay, {"item": item}) + + itemContainer.parent = lay; + //if section, wee need another placeholder + if (item.Kirigami.FormData.isSection) { + var placeHolder = placeHolderComponent.createObject(lay, {"item": item}); + placeHolder.parent = lay; + } + } + } + } + + onChildrenChanged: relayoutTimer.restart(); + + Component.onCompleted: childrenChanged() + Component { + id: itemComponent + Item { + id: container + property var item + enabled: item.enabled + visible: item.visible + implicitWidth: item.implicitWidth + Layout.preferredWidth: item.Layout.preferredWidth + Layout.preferredHeight: Math.max(item.Layout.preferredHeight, item.implicitHeight) + + Layout.alignment: (root.wideMode ? Qt.AlignLeft | Qt.AlignVCenter : Qt.AlignHCenter | Qt.AlignTop) + Layout.fillWidth: item.Kirigami.FormData.isSection + Layout.columnSpan: item.Kirigami.FormData.isSection ? lay.columns : 1 + onItemChanged: { + if (!item) { + container.destroy(); + } + } + onXChanged: item.x = x; + onYChanged: item.y = y; + onWidthChanged: item.width = width; + } + } + Component { + id: placeHolderComponent + Item { + property var item + enabled: item.enabled + visible: item.visible + width: Kirigami.Units.smallSpacing + height: Kirigami.Units.smallSpacing + onItemChanged: { + if (!item) { + labelItem.destroy(); + } + } + } + } + Component { + id: buddyComponent + Kirigami.Heading { + id: labelItem + + property var item + enabled: item.enabled + visible: item.visible + Kirigami.MnemonicData.enabled: item.Kirigami.FormData.buddyFor && item.Kirigami.FormData.buddyFor.activeFocusOnTab + Kirigami.MnemonicData.label: item.Kirigami.FormData.label + text: Kirigami.MnemonicData.decoratedLabel + + level: item.Kirigami.FormData.isSection ? 3 : 5 + + Layout.preferredHeight: item.Kirigami.FormData.label.length > 0 ? implicitHeight : Kirigami.Units.smallSpacing + + Layout.alignment: root.wideMode + ? (Qt.AlignRight | (item.Kirigami.FormData.buddyFor.height > height * 2 ? Qt.AlignTop : Qt.AlignVCenter)) + : (Qt.AlignLeft | Qt.AlignBottom) + verticalAlignment: root.wideMode ? Text.AlignVCenter : Text.AlignBottom + + Layout.topMargin: item.Kirigami.FormData.buddyFor.height > implicitHeight * 2 ? Kirigami.Units.smallSpacing/2 : 0 + onItemChanged: { + if (!item) { + labelItem.destroy(); + } + } + Shortcut { + sequence: labelItem.Kirigami.MnemonicData.sequence + onActivated: item.Kirigami.FormData.buddyFor.forceActiveFocus() + } + } + } +} diff --git a/src/formlayoutattached.h b/src/formlayoutattached.h new file mode 100644 --- /dev/null +++ b/src/formlayoutattached.h @@ -0,0 +1,70 @@ +/* +* Copyright (C) 2017 by Marco Martin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef FORMLAYOUTATTACHED_H +#define FORMLAYOUTATTACHED_H + +#include +#include + +class QQuickItem; + +class FormLayoutAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(bool isSection READ isSection WRITE setIsSection NOTIFY isSectionChanged) + Q_PROPERTY(QQuickItem *buddyFor READ buddyFor NOTIFY buddyForChanged) + +public: + + explicit FormLayoutAttached(QObject *parent = 0); + ~FormLayoutAttached(); + + void setLabel(const QString &text); + QString label() const; + + QString decoratedLabel() const; + + void setIsSection(bool section); + bool isSection() const; + + QQuickItem *buddyFor() const; + + //QML attached property + static FormLayoutAttached *qmlAttachedProperties(QObject *object); + +Q_SIGNALS: + void labelChanged(); + void isSectionChanged(); + void buddyForChanged(); + void mnemonicChanged(); + void decoratedLabelChanged(); + +private: + QString m_label; + QString m_actualDecoratedLabel; + QString m_decoratedLabel; + QPointer m_buddyFor; + bool m_isSection = false; +}; + +QML_DECLARE_TYPEINFO(FormLayoutAttached, QML_HAS_ATTACHED_PROPERTIES) + +#endif // FORMLAYOUTATTACHED_H diff --git a/src/formlayoutattached.cpp b/src/formlayoutattached.cpp new file mode 100644 --- /dev/null +++ b/src/formlayoutattached.cpp @@ -0,0 +1,75 @@ +/* +* Copyright (C) 2017 by Marco Martin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "formlayoutattached.h" +#include +#include + +FormLayoutAttached::FormLayoutAttached(QObject *parent) + : QObject(parent) +{ + m_buddyFor = qobject_cast(parent); + qApp->installEventFilter(this); +} + +FormLayoutAttached::~FormLayoutAttached() +{ +} + +void FormLayoutAttached::setLabel(const QString &text) +{ + if (m_label == text) { + return; + } + + m_label = text; + emit labelChanged(); +} + +QString FormLayoutAttached::label() const +{ + return m_label; +} + +void FormLayoutAttached::setIsSection(bool section) +{ + if (m_isSection == section) { + return; + } + + m_isSection = section; + emit isSectionChanged(); +} + +bool FormLayoutAttached::isSection() const +{ + return m_isSection; +} + +QQuickItem *FormLayoutAttached::buddyFor() const +{ + return m_buddyFor; +} + +FormLayoutAttached *FormLayoutAttached::qmlAttachedProperties(QObject *object) +{ + return new FormLayoutAttached(object); +} + +#include "moc_formlayoutattached.cpp" diff --git a/src/kirigamiplugin.cpp b/src/kirigamiplugin.cpp --- a/src/kirigamiplugin.cpp +++ b/src/kirigamiplugin.cpp @@ -23,6 +23,8 @@ #include "enums.h" #include "desktopicon.h" #include "settings.h" +#include "formlayoutattached.h" +#include "mnemonicattached.h" #include #include @@ -88,9 +90,9 @@ qmlRegisterUncreatableType(uri, 2, 0, "ApplicationHeaderStyle", "Cannot create objects of type ApplicationHeaderStyle"); + //old legacy retrocompatible Theme qmlRegisterSingletonType(componentUrl(QStringLiteral("Theme.qml")), uri, 2, 0, "Theme"); - //Theme changed from a singleton to an attached property - qmlRegisterUncreatableType(uri, 2, 2, "Theme", "Cannot create objects of type Theme, use it as an attached poperty"); + qmlRegisterSingletonType(componentUrl(QStringLiteral("Units.qml")), uri, 2, 0, "Units"); qmlRegisterType(componentUrl(QStringLiteral("Action.qml")), uri, 2, 0, "Action"); @@ -135,6 +137,15 @@ qmlRegisterType(componentUrl(QStringLiteral("AbstractApplicationItem.qml")), uri, 2, 1, "AbstractApplicationItem"); qmlRegisterType(componentUrl(QStringLiteral("ApplicationItem.qml")), uri, 2, 1, "ApplicationItem"); + //2.2 + //Theme changed from a singleton to an attached property + qmlRegisterUncreatableType(uri, 2, 2, "Theme", "Cannot create objects of type Theme, use it as an attached poperty"); + + //2.3 + qmlRegisterType(componentUrl(QStringLiteral("FormLayout.qml")), uri, 2, 3, "FormLayout"); + qmlRegisterUncreatableType(uri, 2, 3, "FormData", "Cannot create objects of type FormData, use it as an attached poperty"); + qmlRegisterUncreatableType(uri, 2, 3, "MnemonicData", "Cannot create objects of type MnemonicData, use it as an attached poperty"); + qmlProtectModule(uri, 2); } diff --git a/src/mnemonicattached.h b/src/mnemonicattached.h new file mode 100644 --- /dev/null +++ b/src/mnemonicattached.h @@ -0,0 +1,75 @@ +/* +* Copyright (C) 2017 by Marco Martin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#ifndef MNEMONICATTACHED_H +#define MNEMONICATTACHED_H + +#include +#include + +class QQuickItem; + +class MnemonicAttached : public QObject +{ + Q_OBJECT + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged) + Q_PROPERTY(QString decoratedLabel READ decoratedLabel NOTIFY decoratedLabelChanged) + Q_PROPERTY(QKeySequence sequence READ sequence NOTIFY sequenceChanged) + +public: + + explicit MnemonicAttached(QObject *parent = 0); + ~MnemonicAttached(); + + void setLabel(const QString &text); + QString label() const; + + void setEnabled(bool enabled); + bool enabled() const; + + QString decoratedLabel() const; + + QKeySequence sequence(); + + //QML attached property + static MnemonicAttached *qmlAttachedProperties(QObject *object); + +protected: + bool eventFilter(QObject *watched, QEvent *e); + void updateSequence(); + +Q_SIGNALS: + void labelChanged(); + void enabledChanged(); + void sequenceChanged(); + void decoratedLabelChanged(); + +private: + QString m_label; + QString m_actualDecoratedLabel; + QString m_decoratedLabel; + bool m_enabled = true; + static QHash s_sequenceToObject; + static QHash s_objectToSequence; +}; + +QML_DECLARE_TYPEINFO(MnemonicAttached, QML_HAS_ATTACHED_PROPERTIES) + +#endif // MnemonicATTACHED_H diff --git a/src/mnemonicattached.cpp b/src/mnemonicattached.cpp new file mode 100644 --- /dev/null +++ b/src/mnemonicattached.cpp @@ -0,0 +1,163 @@ +/* +* Copyright (C) 2017 by Marco Martin +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU Library General Public License as +* published by the Free Software Foundation; either version 2, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Library General Public License for more details +* +* You should have received a copy of the GNU Library General Public +* License along with this program; if not, write to the +* Free Software Foundation, Inc., +* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include "mnemonicattached.h" +#include +#include + +QHash MnemonicAttached::s_sequenceToObject = QHash(); +QHash MnemonicAttached::s_objectToSequence = QHash(); + +MnemonicAttached::MnemonicAttached(QObject *parent) + : QObject(parent) +{ + qApp->installEventFilter(this); +} + +MnemonicAttached::~MnemonicAttached() +{ + if (s_objectToSequence.contains(this)) { + s_sequenceToObject.remove(s_objectToSequence.value(this)); + s_objectToSequence.remove(this); + } +} + +bool MnemonicAttached::eventFilter(QObject *watched, QEvent *e) +{ + Q_UNUSED(watched) + + if (m_decoratedLabel.length() == 0) { + return false; + } + + if (e->type() == QEvent::KeyPress) { + QKeyEvent *ke = static_cast(e); + if (ke->key() == Qt::Key_Alt) { + m_actualDecoratedLabel = m_decoratedLabel; + emit decoratedLabelChanged(); + } + + } else if (e->type() == QEvent::KeyRelease) { + QKeyEvent *ke = static_cast(e); + if (ke->key() == Qt::Key_Alt) { + m_actualDecoratedLabel = m_label; + m_actualDecoratedLabel.replace("&", QString()); + emit decoratedLabelChanged(); + } + } + return false; +} + +void MnemonicAttached::updateSequence() +{ + //forget about old association + if (s_objectToSequence.contains(this)) { + s_sequenceToObject.remove(s_objectToSequence.value(this)); + s_objectToSequence.remove(this); + } + + const QString text = label(); + + if (!m_enabled) { + m_actualDecoratedLabel = text; + m_actualDecoratedLabel.replace("&", QString()); + emit decoratedLabelChanged(); + return; + } + + //case 1: explicitly asked, like &Apply + if (text.contains("&")) { + m_decoratedLabel = text; + m_actualDecoratedLabel = text; + m_decoratedLabel.replace(QRegularExpression("&(.)"), "\\1"); + m_actualDecoratedLabel.replace("&", QString()); + s_sequenceToObject[QKeySequence::mnemonic(text)] = this; + s_objectToSequence[this] = QKeySequence::mnemonic(text); + + //case 2: try to understand by ourselves + } else { + const QChar uC; + for (QChar c : text) { + QKeySequence ks("Alt+"%c); + if (!s_sequenceToObject.contains(ks)) { + s_sequenceToObject[ks] = this; + s_objectToSequence[this] = ks; + m_decoratedLabel = text; + m_decoratedLabel.replace(QString(c), "" % c % ""); + m_actualDecoratedLabel = text; + break; + } + } + + if (s_objectToSequence.contains(this)) { + emit sequenceChanged(); + } + } + + emit decoratedLabelChanged(); +} + +void MnemonicAttached::setLabel(const QString &text) +{ + if (m_label == text) { + return; + } + + m_label = text; + updateSequence(); + emit labelChanged(); +} + +QString MnemonicAttached::decoratedLabel() const +{ + return m_actualDecoratedLabel.length() > 0 ? m_actualDecoratedLabel : m_label; +} + +QString MnemonicAttached::label() const +{ + return m_label; +} + +void MnemonicAttached::setEnabled(bool enabled) +{ + if (m_enabled == enabled) { + return; + } + + m_enabled = enabled; + updateSequence(); + emit enabledChanged(); +} + +bool MnemonicAttached::enabled() const +{ + return m_enabled; +} + +QKeySequence MnemonicAttached::sequence() +{ + return s_objectToSequence.value(this); +} + +MnemonicAttached *MnemonicAttached::qmlAttachedProperties(QObject *object) +{ + return new MnemonicAttached(object); +} + +#include "moc_mnemonicattached.cpp"