File indexing completed on 2024-04-21 03:56:00

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.0-or-later
0005  */
0006 
0007 #ifndef MNEMONICATTACHED_H
0008 #define MNEMONICATTACHED_H
0009 
0010 #include <QObject>
0011 #include <QQuickWindow>
0012 
0013 #include <QQmlEngine>
0014 
0015 /**
0016  * This Attached property is used to calculate automated keyboard sequences
0017  * to trigger actions based upon their text: if an "&" mnemonic is
0018  * used (ie "&Ok"), the system will attempt to assign the desired letter giving
0019  * it priority, otherwise a letter among the ones in the label will be used if
0020  * possible and not conflicting.
0021  * Different kinds of controls will have different priorities in assigning the
0022  * shortcut: for instance the "Ok/Cancel" buttons in a dialog will have priority
0023  * over fields of a FormLayout.
0024  * @see ControlType
0025  *
0026  * Usually the developer shouldn't use this directly as base components
0027  * already use this, but only when implementing a custom graphical Control.
0028  * @since 2.3
0029  */
0030 class MnemonicAttached : public QObject
0031 {
0032     Q_OBJECT
0033     QML_NAMED_ELEMENT(MnemonicData)
0034     QML_ATTACHED(MnemonicAttached)
0035     QML_UNCREATABLE("Cannot create objects of type MnemonicData, use it as an attached property")
0036     /**
0037      * The label of the control we want to compute a mnemonic for, instance
0038      * "Label:" or "&Ok"
0039      */
0040     Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged FINAL)
0041 
0042     /**
0043      * The user-visible final label, which will have the shortcut letter underlined,
0044      * such as "&lt;u&gt;O&lt;/u&gt;k"
0045      */
0046     Q_PROPERTY(QString richTextLabel READ richTextLabel NOTIFY richTextLabelChanged FINAL)
0047 
0048     /**
0049      * The label with an "&" mnemonic in the place which will have the shortcut
0050      * assigned, regardless of whether the & was assigned by the user or automatically generated.
0051      */
0052     Q_PROPERTY(QString mnemonicLabel READ mnemonicLabel NOTIFY mnemonicLabelChanged FINAL)
0053 
0054     /**
0055      * Only if true this mnemonic will be considered for the global assignment
0056      * default: true
0057      */
0058     Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged FINAL)
0059 
0060     /**
0061      * The type of control this mnemonic is attached: different types of controls have different importance and priority for shortcut assignment.
0062      * @see ControlType
0063      */
0064     Q_PROPERTY(MnemonicAttached::ControlType controlType READ controlType WRITE setControlType NOTIFY controlTypeChanged FINAL)
0065 
0066     /**
0067      * The final key sequence assigned, if any: it will be Alt+alphanumeric char
0068      */
0069     Q_PROPERTY(QKeySequence sequence READ sequence NOTIFY sequenceChanged FINAL)
0070 
0071     /**
0072      * True when the user is pressing alt and the accelerators should be shown
0073      *
0074      * @since 5.72
0075      * @since 2.15
0076      */
0077     Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged FINAL)
0078 
0079 public:
0080     enum ControlType {
0081         ActionElement, /**< pushbuttons, checkboxes etc */
0082         DialogButton, /**< buttons for dialogs */
0083         MenuItem, /**< Menu items */
0084         FormLabel, /**< Buddy label in a FormLayout*/
0085         SecondaryControl, /**< Other controls that are considered not much important and low priority for shortcuts */
0086     };
0087     Q_ENUM(ControlType)
0088 
0089     explicit MnemonicAttached(QObject *parent = nullptr);
0090     ~MnemonicAttached() override;
0091 
0092     void setLabel(const QString &text);
0093     QString label() const;
0094 
0095     QString richTextLabel() const;
0096     QString mnemonicLabel() const;
0097 
0098     void setEnabled(bool enabled);
0099     bool enabled() const;
0100 
0101     void setControlType(MnemonicAttached::ControlType controlType);
0102     ControlType controlType() const;
0103 
0104     QKeySequence sequence();
0105 
0106     void setActive(bool active);
0107     bool active() const;
0108 
0109     // QML attached property
0110     static MnemonicAttached *qmlAttachedProperties(QObject *object);
0111 
0112 protected:
0113     void updateSequence();
0114 
0115 Q_SIGNALS:
0116     void labelChanged();
0117     void enabledChanged();
0118     void sequenceChanged();
0119     void richTextLabelChanged();
0120     void mnemonicLabelChanged();
0121     void controlTypeChanged();
0122     void activeChanged();
0123 
0124 private:
0125     QWindow *window() const;
0126 
0127     void onAltPressed();
0128     void onAltReleased();
0129 
0130     void calculateWeights();
0131 
0132     // TODO: to have support for DIALOG_BUTTON_EXTRA_WEIGHT etc, a type enum should be exported
0133     enum {
0134         // Additional weight for first character in string
0135         FIRST_CHARACTER_EXTRA_WEIGHT = 50,
0136         // Additional weight for the beginning of a word
0137         WORD_BEGINNING_EXTRA_WEIGHT = 50,
0138         // Additional weight for a 'wanted' accelerator ie string with '&'
0139         WANTED_ACCEL_EXTRA_WEIGHT = 150,
0140         // Default weight for an 'action' widget (ie, pushbuttons)
0141         ACTION_ELEMENT_WEIGHT = 50,
0142         // Additional weight for the dialog buttons (large, we basically never want these reassigned)
0143         DIALOG_BUTTON_EXTRA_WEIGHT = 300,
0144         // Weight for FormLayout labels (low)
0145         FORM_LABEL_WEIGHT = 20,
0146         // Weight for Secondary controls which are considered less important (low)
0147         SECONDARY_CONTROL_WEIGHT = 10,
0148         // Default weight for menu items
0149         MENU_ITEM_WEIGHT = 250,
0150     };
0151 
0152     // order word letters by weight
0153     int m_weight = 0;
0154     int m_baseWeight = 0;
0155     ControlType m_controlType = SecondaryControl;
0156     QMap<int, QChar> m_weights;
0157 
0158     QString m_label;
0159     QString m_actualRichTextLabel;
0160     QString m_richTextLabel;
0161     QString m_mnemonicLabel;
0162     QKeySequence m_sequence;
0163     bool m_enabled = true;
0164     bool m_active = false;
0165 
0166     QPointer<QQuickWindow> m_window;
0167 
0168     // global mapping of mnemonics
0169     // TODO: map by QWindow
0170     static QHash<QKeySequence, MnemonicAttached *> s_sequenceToObject;
0171 };
0172 
0173 QML_DECLARE_TYPEINFO(MnemonicAttached, QML_HAS_ATTACHED_PROPERTIES)
0174 
0175 #endif // MnemonicATTACHED_H