Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

How to render shortcut text in custom button?



  • Hi Friends,
    I am creating a custom Button that receives an Action, something like this:

            CustomButton {
                action: Action {
                    icon.width: 24
                    icon.height: 24
                    icon.source: "/icons/save.svg"
                    text: qsTr("&Save")
                    onTriggered: {
                        console.log("onTriggered");
                    }
                }
            }
    

    In the custom button I have a Text Item with the action's text:

            Text {
                height: root.action.icon.height
                text: root.action.text
                verticalAlignment: Text.AlignVCenter
                color: area.containsMouse ? palette.brightText : palette.highlightedText
            }
    

    The problem is that the text is rendered as it is "&Save" and I need it to be rendered as "Save" with "S" underlined. As it is with builtin menus and buttons.

    Does anyone knows how to do that?

    Thanks.



  • You can use MenuItem:

    MenuItem {
            width: 100
            height: 30
            text: "&Start"
    }
    

    Docs say its a convenience item based upon AbstractButton.



  • The whole point is not to use MenuItem, The idea is to create a custom Item.



  • @mnesarco Could you add a bit of code in your custom button - specifically in the Text item - to parse the action text into a rich text string? So "&Save" would be transformed to "<b>S</b>ave".

            Text {
                height: root.action.icon.height
                text: transformText(root.action.text)
                ...
                def transformText(t) { ... }
            }
    


  • @mnesarco - I copied your code and ran it but changed CustomButton to Button and it works, the 'S' is underlined

    button-text-underline-capital.JPG



  • @Markkyboy this is funny, the Idea is to create a custom component not to use the builtin Button component that obviously works.



  • @Bob64 That is what I do right now as a workaround. But I wonder if there is a way to do it calling some builtin functionality.



  • I have found this class in Qt sources: QQuickMnemonicLabel it has a function that translate the mnemonic text into the undelined version. But this class is private.

    This is the whole class source:

    #include "qquickmnemoniclabel_p.h"
    
    #include <QtQuick/private/qquicktext_p_p.h>
    
    QT_BEGIN_NAMESPACE
    
    QQuickMnemonicLabel::QQuickMnemonicLabel(QQuickItem *parent)
        : QQuickText(parent)
    {
    }
    
    QString QQuickMnemonicLabel::text() const
    {
        return m_fullText;
    }
    
    void QQuickMnemonicLabel::setText(const QString &text)
    {
        if (m_fullText == text)
            return;
    
        m_fullText = text;
        updateMnemonic();
    }
    
    bool QQuickMnemonicLabel::isMnemonicVisible() const
    {
        return m_mnemonicVisible;
    }
    
    void QQuickMnemonicLabel::setMnemonicVisible(bool visible)
    {
        if (m_mnemonicVisible == visible)
            return;
    
        m_mnemonicVisible = visible;
        updateMnemonic();
    
        if (isComponentComplete())
            forceLayout();
    }
    
    static QTextLayout::FormatRange underlineRange(int start, int length = 1)
    {
        QTextLayout::FormatRange range;
        range.start = start;
        range.length = length;
        range.format.setFontUnderline(true);
        return range;
    }
    
    // based on QPlatformTheme::removeMnemonics()
    void QQuickMnemonicLabel::updateMnemonic()
    {
        QString text(m_fullText.size(), QChar::Null);
        int idx = 0;
        int pos = 0;
        int len = m_fullText.length();
        QList<QTextLayout::FormatRange> formats;
        while (len) {
            if (m_fullText.at(pos) == QLatin1Char('&') && (len == 1 || m_fullText.at(pos + 1) != QLatin1Char('&'))) {
                if (m_mnemonicVisible && (pos == 0 || m_fullText.at(pos - 1) != QLatin1Char('&')))
                    formats += underlineRange(pos);
                ++pos;
                --len;
                if (len == 0)
                    break;
            } else if (m_fullText.at(pos) == QLatin1Char('(') && len >= 4 &&
                       m_fullText.at(pos + 1) == QLatin1Char('&') &&
                       m_fullText.at(pos + 2) != QLatin1Char('&') &&
                       m_fullText.at(pos + 3) == QLatin1Char(')')) {
                // a mnemonic with format "\s*(&X)"
                if (m_mnemonicVisible) {
                    formats += underlineRange(pos + 1);
                } else {
                    int n = 0;
                    while (idx > n && text.at(idx - n - 1).isSpace())
                        ++n;
                    idx -= n;
                    pos += 4;
                    len -= 4;
                    continue;
                }
            }
            text[idx] = m_fullText.at(pos);
            ++pos;
            ++idx;
            --len;
        }
        text.truncate(idx);
    
        QQuickTextPrivate::get(this)->layout.setFormats(formats);
        QQuickText::setText(text);
    }
    
    QT_END_NAMESPACE
    
    

    So, I am afraid that the javascript function solution is the only way to go, ugly but works. There is also a StackOverflow post about this: https://stackoverflow.com/questions/54736428/how-to-style-menubaritem-with-mnemonics-strings-in-qml



  • @mnesarco said in How to render shortcut text in custom button?:

    This is the whole class source:

    That does replace the text, but I don't see it generating a keyboard action. I would have thought the default buttons actually generate a keyboard action shortcut.



  • Apparently there is no builtin solution for this. It must be done in application code. Do I have to mark this topic as Solved?


Log in to reply