Search code examples
qtunicodeqmlqstringqlineedit

Limiting QML TextField length causes multibyte-characters to be trimmed off


I have a QML TextField and want to limit the length to 16 characters.

TextField {
    id: myTextField
    maximumLength: 16
}

Say I enter a multibyte-character like "😊" at the end of a 15 character long string, the emoji gets trimmed and results as �, which is one byte (0x3f in this case). I'm not sure where the 0x3f comes from, because internally the QString works with UTF-16, so this might be the result of some back and forth conversion between UTF-8 and UTF-16.

The only way I see right now to avoid this trimming of multibyte-characters is to implement my own QValidator where I then need to check the length of my string with a Unicode aware library like ICU.

My Question now: Is there any other easier way to avoid the trimming, that I'm missing here?


Solution

  • First, let's look at the 😊 emoji:

    import QtQuick
    import QtQuick.Controls
    Page {
        Column {
            Text { text: "😊" }               // 😊 
            Text { text: "\u{1f60a}" }        // 😊
            Text { text: "\u{d83d}\u{de0a}" } // 😊
            Text { text: "\u{d83d}" }         // �
            Text { text: "\u{de0a}" }         // �
            Text { text: "�" }               // �
            Text { text: "\u{fffd}" }         // �
        }
    }
    

    It is a codepoint character \u{1f60a} which is made up of \u{d83d} and \u{de0a}. Because you set maximumLength to 16 when you paste the emoji you are only getting one of the characters, i.e. \u{d83d} which Qt recognizes as not a character so it replaces it with \u{fffd} which is a placeholder character for "I don't know".

    If you want to limit your input to 16 "characters" but want to eliminate that "I don't know" character you can use RegularExpressionValidator.

            maximumLength: 16
            validator: RegularExpressionValidator {
                regularExpression: /[^�]{0,16}/u
            }
    

    Alternatively, if you want to limit yourself to 16 codepoint characters, you can do the following:

            validator: RegularExpressionValidator {
                regularExpression: /.{0,16}/u
            }
    

    Here's a fully working example of the former:

    import QtQuick
    import QtQuick.Controls
    Page {
        TextField {
            id: myTextField
            anchors.centerIn: parent
            width: parent.width / 2
            text: "😊😊😊😊😊😊😊😊"
            maximumLength: 16
            validator: RegularExpressionValidator {
                regularExpression: /[^�]{0,16}/u
            }
        }
        Text {
            anchors.horizontalCenter: parent.horizontalCenter
            y: parent.height * 3 / 4
            text: myTextField.length
        }
    }
    

    You can Try it Online!