#include "keyboard.h" #include "LineEditWithKeyboard.h" #include #include #include #include #include #include #include #include #include #include #include "UiConfig.h" #include "UiTools.h" //∧ len:3 #define KEY_COL_UNIT 4 #define KEY_HIDE " ▼ " #define KEY_UPPER "△" #define KEY_LOWER "∧" #define KEY_SYMBOL " !@# " #define KEY_SYMBOL_BACK "Back" #define KEY_CN "中" #define KEY_EN "En" #define KEY_SPACE " Space " #define KEY_BACKSPACE " ← " #define KEY_ENTER "Enter" //添加按键时需要注意不要超出头文件中定义的按键个数 static const char* lower_letter[] = {KEY_HIDE, "1","2","3","4","5","6","7","8","9","0", "q","w","e","r","t","y","u","i","o","p", "a","s","d","f","g","h","j","k","l", KEY_UPPER,"z","x","c","v","b","n","m",KEY_BACKSPACE, KEY_CN,KEY_SYMBOL,KEY_SPACE,".",KEY_ENTER}; static const char* upper_letter[] = {KEY_HIDE, "1","2","3","4","5","6","7","8","9","0", "Q","W","E","R","T","Y","U","I","O","P", "A","S","D","F","G","H","J","K","L", KEY_LOWER,"Z","X","C","V", "B","N","M",KEY_BACKSPACE, KEY_CN,KEY_SYMBOL,KEY_SPACE,".",KEY_ENTER}; static const char* cn_letter[] = {KEY_HIDE, "-","=","[","]","\\",";","\'",",","?","/", "!","@","#","$","%","^","&&","*","(",")", "_","+","{","}","|",":","\"","<",">", "~","·","×","÷","「","」","✘","√",KEY_BACKSPACE, KEY_CN,KEY_SYMBOL_BACK,KEY_SPACE,".",KEY_ENTER}; static const int COL_NUM_EACH_ROW[] = {1, 10, 10, 9, 9, 5}; static const int KEY_NUM = sizeof(lower_letter) / sizeof(*lower_letter); #if 0 QPushButton* keyboard::m_btn[KEYS_QUANTITY_MAX] = {nullptr}; //chinese support QVector* keyboard::m_btnsCnCdd = nullptr; QLineEdit* keyboard::m_editCn = nullptr; QPushButton* keyboard::m_btnCnPrePage = nullptr; QPushButton* keyboard::m_btnCnNextPage = nullptr; #endif QFile* keyboard::m_pinyinFile = nullptr; QRegExp* keyboard::m_regExp = nullptr; QMultiMap* keyboard::m_pinyinMap = nullptr; keyboard* keyboard::m_instance = nullptr; keyboard::enKeyboardStatus keyboard::m_status = enKeyboardNotGen; keyboard* keyboard::GetInstance(QWidget *parent) { if(nullptr == m_instance && nullptr != parent){ qDebug() << "create keyboard"; m_status = enKeyboardCreating; m_instance = new keyboard(parent); m_instance->popup(); m_status = enKeyboardCreated; } if(parent){ QObject* recv = static_cast(parent); if(m_instance->receiver() != recv){ m_instance->setReceiver(static_cast(parent)); } } return m_instance; } void keyboard::close() { if(m_instance){ if(enKeyboardCreated == m_status){ qDebug() << "close keyboard"; m_instance->slotClose(); } else { qDebug() << "close keyboard later"; QTimer::singleShot(500, m_instance, SLOT(slotClose())); } } } void keyboard::preLoad() { if(nullptr == m_instance){ qDebug() << "keyboard pre load"; keyboard(); } } keyboard::keyboard(QWidget *parent) : QDialog(parent), m_receiver(nullptr) { m_status = enKeyboardNotGen; m_editCn = nullptr; m_btnCnPrePage = nullptr; m_btnCnNextPage = nullptr; setReceiver(static_cast(parent)); qDebug() << "keyboard()"; QPalette palette(this->palette()); palette.setColor(QPalette::ButtonText, Qt::white); const int chooseBtnHeight = UiConfig::GetInstance()->getUiWidth() / 16; m_btnsCnCdd.resize(8); QHBoxLayout* hLayoutCdd = new QHBoxLayout(); for(auto &b : m_btnsCnCdd) { //if(nullptr == b){ b = new QPushButton(); b->setFixedWidth(static_cast(UiConfig::GetInstance()->getUiWidth()) / m_btnsCnCdd.size()); b->setPalette(palette); b->setStyleSheet("background-color: transparent;"); b->setFlat(true); b->setFixedSize(chooseBtnHeight * 2, chooseBtnHeight); connect(b, SIGNAL(clicked(bool)), this, SLOT(slotCandidateBtnClicked())); //} hLayoutCdd->addWidget(b, 1, Qt::AlignLeft); } hLayoutCdd->addWidget(new QLabel(this), 100, Qt::AlignRight); if(nullptr == m_editCn){ m_editCn = new QLineEdit(); m_editCn->setReadOnly(true); palette.setColor(QPalette::Text, Qt::white); m_editCn->setPalette(palette); m_editCn->setFixedSize(chooseBtnHeight * 4, chooseBtnHeight); m_editCn->setStyleSheet("background:transparent;border-width:0;border-style:outset"); } if(nullptr == m_btnCnPrePage){ m_btnCnPrePage = new QPushButton(); m_btnCnPrePage->setText(" ↑ "); m_btnCnPrePage->setPalette(palette); m_btnCnPrePage->setFixedSize(chooseBtnHeight * 2, chooseBtnHeight); m_btnCnPrePage->setStyleSheet("background-color: rgb(57, 53, 50);"); connect(m_btnCnPrePage, SIGNAL(clicked(bool)), this, SLOT(slotPageTurnBtnClicked())); } if(nullptr == m_btnCnNextPage){ m_btnCnNextPage = new QPushButton(); m_btnCnNextPage->setText(" ↓ "); m_btnCnNextPage->setPalette(palette); m_btnCnNextPage->setFixedSize(chooseBtnHeight * 2, chooseBtnHeight); m_btnCnNextPage->setStyleSheet("background-color: rgb(57, 53, 50);"); connect(m_btnCnNextPage, SIGNAL(clicked(bool)), this, SLOT(slotPageTurnBtnClicked())); } QVBoxLayout* vLayout = new QVBoxLayout(); vLayout->addLayout(hLayoutCdd); QHBoxLayout* hLayoutLine[ROW_NUM] = {nullptr}; for(int i = 0; i < ROW_NUM; i ++) { hLayoutLine[i] = new QHBoxLayout(); hLayoutLine[i]->setMargin(0); //hLayoutLine[i]->setSpacing(15); if(i < ROW_NUM - 1){ //最后一列不需要 hLayoutLine[i]->addStretch(0); } } hLayoutLine[0]->addWidget(m_editCn, 2, Qt::AlignLeft); hLayoutLine[0]->addWidget(m_btnCnPrePage, 1, Qt::AlignLeft); hLayoutLine[0]->addWidget(m_btnCnNextPage, 1, Qt::AlignLeft); int col = 0; int row = 0; int colMax = 0; //最大列数 int colTaken = 0; palette.setColor(QPalette::ButtonText, Qt::white); QFont ft; ft.setPointSize(UiConfig::GetInstance()->isRkDevice() ? 12 : 24); for(int i = 0; i < KEY_NUM; i ++) { m_btn[i] = new QPushButton(this); m_btn[i]->setPalette(palette); m_btn[i]->setStyleSheet("background-color: rgb(57, 53, 50);"); m_btn[i]->setFocusPolicy(Qt::NoFocus); m_btn[i]->setFont(ft); //m_btn[i]->setFlat(true); connect(m_btn[i], SIGNAL(clicked(bool)), this, SLOT(slotBtnClicked())); connect(m_btn[i], SIGNAL(pressed()), this, SLOT(slotBtnPressed())); connect(m_btn[i], SIGNAL(released()), this, SLOT(slotBtnReleased())); int btnCol = (strlen(lower_letter[i]) / KEY_COL_UNIT) + 1; hLayoutLine[row]->addWidget(m_btn[i]); colTaken += btnCol; col++; if(COL_NUM_EACH_ROW[row] == col) { col = 0; row++; if(colTaken > colMax){ colMax = colTaken; } colTaken = 0; } } int unit = (UiConfig::GetInstance()->getUiWidth() - ((colMax - 1) * SPACE)) / colMax; for(int i = 0; i < KEY_NUM; i ++) { int btnCol = (strlen(lower_letter[i]) / KEY_COL_UNIT) + 1; m_btn[i]->setFixedSize(unit * btnCol, static_cast(unit)); } for(int i = 0; i < ROW_NUM; i ++) { if(i > 0 && i < ROW_NUM - 1){ //首行和尾行不需要 hLayoutLine[i]->addStretch(0); } vLayout->addLayout(hLayoutLine[i]); } vLayout->setSpacing(5);//10 vLayout->setContentsMargins(0, 10, 0, 10); setLayout(vLayout); setFocusPolicy(Qt::NoFocus); setWindowFlags( Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool | Qt::WindowDoesNotAcceptFocus); if(nullptr == m_pinyinFile){ m_pinyinFile = new QFile(); m_pinyinFile->setFileName(":/res/inputCn/pinyin-utf8"); //设置QFile的名称 if(!m_pinyinFile->open(QIODevice::ReadOnly) ){ //只读模式,打开文件 QMessageBox::warning(nullptr, "pinyin file", "can't load"); //打开失败,则报错 } } if(nullptr == m_regExp){ m_regExp = new QRegExp(); m_regExp->setCaseSensitivity(Qt::CaseSensitive); //设置正则表达式的参数,Qt::CaseInsensitive,大小写敏感 m_regExp->setPattern(QString("([a-z]+)")); //获得正则本身,获取a-z } if(nullptr == m_pinyinMap){ m_pinyinMap = new QMultiMap(); while(!m_pinyinFile->atEnd()) { QByteArray lineData = m_pinyinFile->readLine(); //读取一行 //qDebug() << "line:" << QString(data.data()); int ret = m_regExp->indexIn(QString(lineData.data()), 0, QRegExp::CaretAtZero); //进行匹配,如果成功则返回index,不成功返回-1 ,data.data()是读取到的一行数据,返回值应该是匹配到的位置 //qDebug() << "ret =" << ret; //qDebug() << "m_regExp.cap(1):" << m_regExp.cap(1) << "QString(lineData.data()).left(ret):" << QString(lineData.data()).left(ret); if("\r\n" != QString(lineData.data()).left(ret)){ m_pinyinMap->insert(m_regExp->cap(1), QString(lineData.data()).left(ret)); //将mmap对象的成员初始化;key是字母,value是行字 } } m_pinyinFile->close(); } setInputMethodCn(false); setAttribute(Qt::WA_DeleteOnClose); setFixedWidth(UiConfig::GetInstance()->getUiWidth()); m_labelTip = new QLabel(this); m_labelTip->setFixedSize(unit, static_cast(unit * 1.5)); m_labelTip->setFont(ft); m_labelTip->setStyleSheet("background-color: rgb(15, 116, 248);"); m_labelTip->setAlignment(Qt :: AlignCenter); m_labelTip->hide(); m_timer = new QTimer(this); connect(m_timer, SIGNAL(timeout()), this, SLOT(slotTimer())); shiftkeyboard(2); } keyboard::~keyboard() { qDebug() << "~keyboard()"; m_instance = nullptr; } void keyboard::setInputMethodCn(bool setCn) { if(setCn){ m_editCn->clear(); m_editCn->show(); } else { for(auto &b : m_btnsCnCdd){ b->hide(); } m_editCn->hide(); m_btnCnPrePage->hide(); m_btnCnNextPage->hide(); } isInputMethodCn = setCn; } void keyboard::shiftkeyboard(int caps) { const char** letter = nullptr; if(0 == caps){ letter = upper_letter; } else if (1 == caps) { letter = cn_letter; } else if (2 == caps) { letter = lower_letter; } for(int i = 0; i < KEY_NUM; i ++){ if(m_btn[i]->text().isEmpty()){ //if has icon, clear first m_btn[i]->setIcon(QIcon()); } if(QString(letter[i]) == KEY_BACKSPACE){ m_btn[i]->setText(""); //m_btn[i]->setIcon(QIcon(":res/image/pwd_del_large.png")); setButtonBackImage(m_btn[i], ":res/image/pwd_del_large_black.png", m_btn[i]->height() / 2 , static_cast(m_btn[i]->height() * 0.34));//w/2*(24/35) m_btn[i]->setStyleSheet("background-color: rgb(57, 53, 50);"); m_btn[i]->setProperty("key", letter[i]); } else if(QString(letter[i]) == KEY_UPPER){ m_btn[i]->setText(""); //m_btn[i]->setIcon(QIcon(":res/image/shift_large.png")); setButtonBackImage(m_btn[i], ":res/image/shift_large_black.png", m_btn[i]->height() / 2, m_btn[i]->height() / 2); m_btn[i]->setStyleSheet("background-color: rgb(57, 53, 50);"); m_btn[i]->setProperty("key", letter[i]); } else if(QString(letter[i]) == KEY_LOWER){ m_btn[i]->setText(""); //m_btn[i]->setIcon(QIcon(":res/image/shift_lock_large.png")); setButtonBackImage(m_btn[i], ":res/image/shift_lock_large_black.png", m_btn[i]->height() / 2, m_btn[i]->height() / 2); m_btn[i]->setStyleSheet("background-color: rgb(57, 53, 50);"); m_btn[i]->setProperty("key", letter[i]); } else{ m_btn[i]->setText(letter[i]); } } } void keyboard::slotBtnPressed() { //qDebug() << "unit:" << dynamic_cast(sender())->width(); QPushButton* btn = dynamic_cast(sender()); if(btn){ for(int i = 0; i < KEY_NUM - 4; i ++){ if(btn == m_btn[i]){ int len = strlen(btn->text().toStdString().c_str()); if(len > 0 && len <= 3){ m_labelTip->setText(btn->text()); m_labelTip->move(btn->x(), btn->y() - m_labelTip->height()); m_labelTip->show(); } } } m_curPressedKey = btn; m_timer->start(500); } } void keyboard::slotBtnReleased() { m_labelTip->hide(); m_timer->stop(); m_curPressedKey = nullptr; } void keyboard::slotBtnClicked() { for(int i = 0; i < KEY_NUM; i ++){ if(sender() == m_btn[i]){ keyPress(m_btn[i]); break; } } } void keyboard::slotPageTurnBtnClicked() { if(sender() == m_btnCnPrePage){ changePage(--m_iCddPageIndex); } else if (sender() == m_btnCnNextPage) { changePage(++m_iCddPageIndex); } } void keyboard::slotCandidateBtnClicked() { for(auto &b : m_btnsCnCdd){ if(sender() == b){ emit signalWordChoose(b->text()); m_editCn->clear(); for(auto &btn : m_btnsCnCdd){ btn->hide(); } m_btnCnPrePage->hide(); m_btnCnNextPage->hide(); break; } } } void keyboard::slotClose() { done(0); } void keyboard::slotTimer() { if(m_curPressedKey){ m_timer->start(100); keyPress(m_curPressedKey); } } void keyboard::paintEvent(QPaintEvent *event) { QPainter painter(this); painter.fillRect(this->rect(), QColor(0, 0, 0,/*0x1f, 0x28, 0x41,*/ 255)); //QColor最后一个参数代表背景的透明度 return QWidget::paintEvent(event); } void keyboard::keyPress(const QPushButton* btn) { if(!btn){ return; } QString str = btn->text(); if(str.isEmpty()){ str = btn->property("key").toString(); } QKeyEvent* event = nullptr; if(str == KEY_BACKSPACE){ event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier); }else if(str == KEY_ENTER){ event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Enter, Qt::NoModifier); }else if(str == KEY_UPPER){ shiftkeyboard(0);//upper_letter }else if(str == KEY_LOWER){ shiftkeyboard(2);//lower_letter }else if(str == KEY_CN){ shiftkeyboard(2); for(int j = 0; j < KEY_NUM; j ++){ if(m_btn[j]->text() == KEY_CN){ m_btn[j]->setText(KEY_EN); } } setInputMethodCn(true); }else if(str == KEY_EN){ for(int j = 0; j < KEY_NUM; j ++){ if(m_btn[j]->text() == KEY_EN){ m_btn[j]->setText(KEY_CN); } } setInputMethodCn(false); }else if(str == KEY_HIDE){ slotClose(); }else if(str == KEY_SYMBOL){ shiftkeyboard(1); }else if(str == KEY_SYMBOL_BACK){ shiftkeyboard(2); }else{ if(str == KEY_SPACE) str = " "; else if (str == "&&") { str = "&"; } event = new QKeyEvent(QEvent::KeyPress, 0, Qt::NoModifier, str); } if(isInputMethodCn){ if(str >= "a" && str <= "z"){ //qt5.5 does not have isLower() func m_editCn->setText(m_editCn->text() + str); matching(m_editCn->text()); }else if (str == KEY_BACKSPACE) { if("" != m_editCn->text()){ m_editCn->setText(m_editCn->text().remove(m_editCn->text().length() - 1, 1)); matching(m_editCn->text()); }else{ QApplication::sendEvent(m_receiver, new QKeyEvent(QEvent::KeyPress, Qt::Key_Backspace, Qt::NoModifier)); } }else { if(nullptr != event){ QApplication::sendEvent(m_receiver, event); } } }else{ if(nullptr != event){ QApplication::sendEvent(m_receiver, event); } } static_cast(m_receiver)->setFocus(); } void keyboard::popup(const int posToBottom) { qDebug() << "keyboard popup"; show(); move(0, UiConfig::GetInstance()->getUiHeight() - height() - posToBottom); #if 0 qDebug() << "child: " << children().size(); QObjectList& childList = const_cast(children()); childList.clear(); qDebug() << "****************child: " << children().size(); #endif } void keyboard::matching(const QString& userInput) { //qDebug() << "input:" << userInput; QList list = m_pinyinMap->values(userInput); int foundQty = list.size(); //qDebug() << "list:" << foundQty; m_pinyinList.clear(); #if 0 for (QList::const_reverse_iterator itr = list.rbegin(); itr != list.rend(); ++itr){ m_pinyinList.append(*itr); } #else for(int i=0; ihide(); } const int matchQty = m_pinyinList.size(); //qDebug() << "matchQty:" << matchQty; if(matchQty){ const int btnQty = m_btnsCnCdd.size(); const int showQty = matchQty - (index * btnQty) > btnQty ? btnQty : matchQty - (index * btnQty); for(int i=0; iisHidden()){ m_btnsCnCdd.at(i)->show(); } m_btnsCnCdd.at(i)->setText(m_pinyinList.value(i + index * btnQty)); //qDebug() << "word:" << m_pinyinList.value(i + index * btnQty); } m_btnCnPrePage->show(); m_btnCnNextPage->show(); m_btnCnPrePage->setEnabled(0 != index); m_btnCnNextPage->setEnabled( (showQty == btnQty) && (matchQty - ((index + 1) * btnQty)) ); } else { m_btnCnPrePage->hide(); m_btnCnNextPage->hide(); } } void keyboard::setReceiver(QObject* receiver) { if(nullptr != receiver){ if(m_receiver){ disconnect(this, SIGNAL(signalWordChoose(QString)), m_receiver, SLOT(slotWordChoose(QString))); } m_receiver = receiver; connect(this, SIGNAL(signalWordChoose(QString)), m_receiver, SLOT(slotWordChoose(QString))); } } QObject* keyboard::receiver() const { return m_receiver; }