Qt Quick - 自定义组件/虚拟键盘

/*******************************************
 * File: VirtualKeyboard.qml
 * Describe: QML虚拟键盘组件
 * Author: Xiangfu DING
 * Time: 2025-02-26
*******************************************/
import QtQuick 2.9
import QtQuick.Window 2.3
import QtQuick.Controls 2.1
import QtQuick.Layouts 1.3
import QtQuick.Dialogs 1.2

/*
    使用示例:
    // 虚拟键盘
    VirtualKeyboard {
        id: virtualKeyboard
        anchors.horizontalCenter: parent.horizontalCenter
        y: parent.height-virtualKeyboard.height-35
        z: 100
    }

    TextField {
        id: digitsField
        width: parent.width
        placeholderText: "Digits only field"
        focus: false
        onFocusChanged: {
            if (focus) {
                virtualKeyboard.setFocusedItem(digitsField)
            }
        }
    }
*/

// 自定义虚拟键盘
Rectangle {
    id: virtualKeyboard
    width: 720
    height: 270
    radius: 10
    color: "black"
    visible: false

    // 焦点控件
    property Item focusedItem: null
    // 设置焦点控件的函数
    function setFocusedItem(item) {
        focusedItem = item;
        isUpper = false
        isEnglish = true
        page = 1
        virtualKeyboard.visible = true
    }

    property real keyWidth: (virtualKeyboard.width - keySpacing*11) / 10
    property real keyHeight: (virtualKeyboard.height - keySpacing*5) / 4
    property int keyRadius: 6
    property int keySpacing: 10

    property bool isUpper: false            // 是否大写
    property bool isEnglish: true           // 是否英文
    property int page: 1                    // 字符页面
    property int pixelSize: 16              // 字体大小

    property var en_line1_lower: ["q", "w", "e", "r", "t", "y", "u", "i", "o", "p"]
    property var en_line1_upper: ["Q", "W", "E", "R", "T", "Y", "U", "I", "O", "P"]
    property var en_line2_lower: ["a", "s", "d", "f", "g", "h", "j", "k", "l"]
    property var en_line2_upper: ["A", "S", "D", "F", "G", "H", "J", "K", "L"]
    property var en_line3_lower: ["z", "x", "c", "v", "b", "n", "m"]
    property var en_line3_upper: ["Z", "X", "C", "V", "B", "N", "M"]

    property var char_page1_line1: ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"]
    property var char_page1_line2: ["~", "-", "+", ";", ":", "_", "=", "|", "\\"]
    property var char_page1_line3: ["`", ",", ".", "<", ">", "/", "?"]

    property var char_page2_line1: ["!", "@", "#", "$", "%", "^", "&", "*", "(", ")"]
    property var char_page2_line2: ["[", "]", "{", "}", "'", "\"", "I", "II", "III"]
    property var char_page2_line3: ["IV", "V", "VI", "VII", "VIII", "IX", "X"]

    ColumnLayout {
        anchors.fill: parent
        anchors.topMargin: keySpacing
        anchors.bottomMargin: keySpacing
        Layout.leftMargin: keySpacing
        Layout.rightMargin: keySpacing
        spacing: keySpacing
        // 第一行
        Row {
            Layout.alignment: Qt.AlignHCenter
            Layout.preferredWidth: parent.width-keySpacing*2
            Layout.preferredHeight: keyHeight
            spacing: keySpacing
            Repeater {
                model: {
                    if (isEnglish) {
                        isUpper ? en_line1_upper : en_line1_lower
                    } else {
                        page == 1 ? char_page1_line1 : char_page2_line1
                    }
                }
                Rectangle {
                    width: keyWidth
                    height: parent.height
                    radius: keyRadius
                    color: area1.pressed ? "#2A2826" : "#383533"
                    Text {
                        anchors.fill: parent
                        font.pixelSize: pixelSize
                        verticalAlignment: Text.AlignVCenter
                        horizontalAlignment: Text.AlignHCenter
                        color: area1.pressed ? "#5D4B37" : "#FFFFFF"
                        text: modelData
                    }
                    MouseArea {
                        id: area1
                        anchors.fill: parent
                        focus: false
                        onClicked: {
                            if (focusedItem) {
                                var cursorPos = focusedItem.cursorPosition
                                var currentText = focusedItem.text
                                focusedItem.text = currentText.slice(0, cursorPos) + modelData + currentText.slice(cursorPos)
                                focusedItem.cursorPosition = cursorPos + 1
                            }
                        }
                    }
                }
            }
        }
        // 第二行
        Row {
            Layout.alignment: Qt.AlignHCenter
            Layout.preferredWidth: parent.width-keySpacing*3-keyWidth
            Layout.preferredHeight: keyHeight
            spacing: keySpacing
            Repeater {
                model: {
                    if (isEnglish) {
                        isUpper ? en_line2_upper : en_line2_lower
                    } else {
                        page == 1 ? char_page1_line2 : char_page2_line2
                    }
                }
                Rectangle {
                    width: keyWidth
                    height: parent.height
                    radius: keyRadius
                    color: area2.pressed ? "#2A2826" : "#383533"
                    Text {
                        anchors.fill: parent
                        font.pixelSize: pixelSize
                        verticalAlignment: Text.AlignVCenter
                        horizontalAlignment: Text.AlignHCenter
                        color: area2.pressed ? "#5D4B37" : "#FFFFFF"
                        text: modelData
                    }
                    MouseArea {
                        id: area2
                        anchors.fill: parent
                        focus: false
                        onClicked: {
                            if (focusedItem) {
                                var cursorPos = focusedItem.cursorPosition
                                var currentText = focusedItem.text
                                focusedItem.text = currentText.slice(0, cursorPos) + modelData + currentText.slice(cursorPos)
                                focusedItem.cursorPosition = cursorPos + 1
                            }
                        }
                    }
                }
            }
        }
        // 第三行
        Row {
            Layout.alignment: Qt.AlignHCenter
            Layout.preferredWidth: parent.width-keySpacing*2
            Layout.preferredHeight: keyHeight
            spacing: keySpacing
            // shift
            Rectangle {
                width: keyWidth+keyWidth/2+keySpacing/2
                height: parent.height
                radius: keyRadius
                color: area_shift.pressed ? "#2A2826" : "#383533"
                Text {
                    anchors.fill: parent
                    font.pixelSize: pixelSize
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignHCenter
                    color: area_shift.pressed ? "#5D4B37" : (isEnglish && isUpper ? "#239B56" : "#FFFFFF")
                    text: isEnglish ? "Shift" : (page == 1 ? "1/2" : "2/2")
                }
                MouseArea {
                    id: area_shift
                    anchors.fill: parent
                    focus: false
                    onClicked: {
                        if (isEnglish) {
                            isUpper = !isUpper
                        } else {
                            page == 1 ? (page = 2) : (page = 1)
                        }
                    }
                }
            }
            Repeater {
                model: {
                    if (isEnglish) {
                        isUpper ? en_line3_upper : en_line3_lower
                    } else {
                        page == 1 ? char_page1_line3 : char_page2_line3
                    }
                }
                Rectangle {
                    width: keyWidth
                    height: parent.height
                    radius: keyRadius
                    color: area3.pressed ? "#2A2826" : "#383533"
                    Text {
                        anchors.fill: parent
                        font.pixelSize: pixelSize
                        verticalAlignment: Text.AlignVCenter
                        horizontalAlignment: Text.AlignHCenter
                        color: area3.pressed ? "#5D4B37" : "#FFFFFF"
                        text: modelData
                    }
                    MouseArea {
                        id: area3
                        anchors.fill: parent
                        focus: false
                        onClicked: {
                            if (focusedItem) {
                                var cursorPos = focusedItem.cursorPosition
                                var currentText = focusedItem.text
                                focusedItem.text = currentText.slice(0, cursorPos) + modelData + currentText.slice(cursorPos)
                                focusedItem.cursorPosition = cursorPos + 1
                            }
                        }
                    }
                }
            }
            // backspace
            Rectangle {
                width: keyWidth+keyWidth/2+keySpacing/2
                height: parent.height
                radius: keyRadius
                color: area_backspace.pressed ? "#2A2826" : "#383533"
                Text {
                    anchors.fill: parent
                    font.pixelSize: pixelSize
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignHCenter
                    color: area_backspace.pressed ? "#5D4B37" : "#FFFFFF"
                    text: "Backspace"
                }
                MouseArea {
                    id: area_backspace
                    anchors.fill: parent
                    focus: false
                    onClicked: {
                        if (focusedItem && focusedItem.text.length > 0) {
                            var currentText = focusedItem.text
                            var cursorPos = focusedItem.cursorPosition
                            if (cursorPos > 0) {
                                focusedItem.text = currentText.slice(0, cursorPos - 1) + currentText.slice(cursorPos)
                                focusedItem.cursorPosition = cursorPos - 1
                            }
                        }
                    }
                }
            }
        }
        // 第四行
        Row {
            Layout.alignment: Qt.AlignHCenter
            Layout.preferredWidth: parent.width-keySpacing*2
            Layout.preferredHeight: keyHeight
            spacing: keySpacing
            // switch
            Rectangle {
                width: keyWidth+keyWidth/2+keySpacing/2
                height: parent.height
                radius: keyRadius
                color: area_switch.pressed ? "#2A2826" : "#383533"
                Text {
                    anchors.fill: parent
                    font.pixelSize: pixelSize
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignHCenter
                    color: area_switch.pressed ? "#5D4B37" : "#FFFFFF"
                    text: isEnglish ? "&123" : "ABC"
                }
                MouseArea {
                    id: area_switch
                    anchors.fill: parent
                    focus: false
                    onClicked: isEnglish = !isEnglish
                }
            }
            // space
            Rectangle {
                width: keyWidth*5+keySpacing*4+keyWidth/2+keySpacing/2
                height: parent.height
                radius: keyRadius
                color: area_space.pressed ? "#2A2826" : "#383533"
                Text {
                    anchors.fill: parent
                    font.pixelSize: pixelSize
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignHCenter
                    color: area_space.pressed ? "#5D4B37" : "#FFFFFF"
                    text: "空 格"
                }
                MouseArea {
                    id: area_space
                    anchors.fill: parent
                    focus: false
                    onClicked: {
                        if (focusedItem) {
                            var cursorPos = focusedItem.cursorPosition
                            var currentText = focusedItem.text
                            focusedItem.text = currentText.slice(0, cursorPos) + " " + currentText.slice(cursorPos)
                            focusedItem.cursorPosition = cursorPos + 1
                        }
                    }
                }
            }
            // clear
            Rectangle {
                width: keyWidth+keyWidth/2+keySpacing/2
                height: parent.height
                radius: keyRadius
                color: area_clear.pressed ? "#2A2826" : "#383533"
                Text {
                    anchors.fill: parent
                    font.pixelSize: pixelSize
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignHCenter
                    color: area_clear.pressed ? "#5D4B37" : "#FFFFFF"
                    text: "Clear"
                }
                MouseArea {
                    id: area_clear
                    anchors.fill: parent
                    focus: false
                    onClicked: {
                        focusedItem.text = ""
                    }
                }
            }

            // hide
            Rectangle {
                width: keyWidth+keyWidth/2+keySpacing/2
                height: parent.height
                radius: keyRadius
                color: area_hide.pressed ? "#2A2826" : "#383533"
                Text {
                    anchors.fill: parent
                    font.pixelSize: pixelSize
                    verticalAlignment: Text.AlignVCenter
                    horizontalAlignment: Text.AlignHCenter
                    color: area_hide.pressed ? "#5D4B37" : "#FFFFFF"
                    text: "收 起"
                }
                MouseArea {
                    id: area_hide
                    anchors.fill: parent
                    onClicked: {
                        if (focusedItem) {
                            focusedItem.focus = false;
                        }
                        virtualKeyboard.visible = false
                    }
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值