From ac0f5bc32569327b0078ccce9b08b15960dab4a9 Mon Sep 17 00:00:00 2001 From: peri4 Date: Wed, 29 May 2024 21:42:16 +0300 Subject: [PATCH] add QAD::VirtualKeyboard library, vkbd for widgets with JSON-based description --- CMakeLists.txt | 4 +- cmake/FindQAD.cmake | 5 +- icons/virtual_keyboard.png | Bin 0 -> 634 bytes libs/virtual_keyboard/CMakeLists.txt | 4 + libs/virtual_keyboard/default.json | 99 +++++++++++ libs/virtual_keyboard/plugin/CMakeLists.txt | 1 + .../plugin/qad_virtual_keyboard.cpp | 17 ++ .../plugin/qad_virtual_keyboard.h | 24 +++ .../plugin/virtual_keyboard_plugin.cpp | 62 +++++++ .../plugin/virtual_keyboard_plugin.h | 36 ++++ libs/virtual_keyboard/virtual_keyboard.cpp | 166 ++++++++++++++++++ libs/virtual_keyboard/virtual_keyboard.h | 54 ++++++ libs/virtual_keyboard/virtual_keyboard.qrc | 8 + libs/virtual_keyboard/virtual_keyboard.ui | 74 ++++++++ .../virtual_keyboard_layout.cpp | 72 ++++++++ .../virtual_keyboard_layout.h | 39 ++++ .../virtual_keyboard_layout_page.cpp | 154 ++++++++++++++++ .../virtual_keyboard_layout_page.h | 55 ++++++ 18 files changed, 871 insertions(+), 3 deletions(-) create mode 100644 icons/virtual_keyboard.png create mode 100644 libs/virtual_keyboard/CMakeLists.txt create mode 100644 libs/virtual_keyboard/default.json create mode 100644 libs/virtual_keyboard/plugin/CMakeLists.txt create mode 100644 libs/virtual_keyboard/plugin/qad_virtual_keyboard.cpp create mode 100644 libs/virtual_keyboard/plugin/qad_virtual_keyboard.h create mode 100644 libs/virtual_keyboard/plugin/virtual_keyboard_plugin.cpp create mode 100644 libs/virtual_keyboard/plugin/virtual_keyboard_plugin.h create mode 100644 libs/virtual_keyboard/virtual_keyboard.cpp create mode 100644 libs/virtual_keyboard/virtual_keyboard.h create mode 100644 libs/virtual_keyboard/virtual_keyboard.qrc create mode 100644 libs/virtual_keyboard/virtual_keyboard.ui create mode 100644 libs/virtual_keyboard/virtual_keyboard_layout.cpp create mode 100644 libs/virtual_keyboard/virtual_keyboard_layout.h create mode 100644 libs/virtual_keyboard/virtual_keyboard_layout_page.cpp create mode 100644 libs/virtual_keyboard/virtual_keyboard_layout_page.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e965e34..343380c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,8 @@ cmake_policy(SET CMP0017 NEW) # need include() with .cmake cmake_policy(SET CMP0072 NEW) # FindOpenGL prefers GLVND by default project(QAD) set(QAD_MAJOR 2) -set(QAD_MINOR 28) -set(QAD_REVISION 1) +set(QAD_MINOR 29) +set(QAD_REVISION 0) set(QAD_SUFFIX ) set(QAD_COMPANY SHS) set(QAD_DOMAIN org.SHS) diff --git a/cmake/FindQAD.cmake b/cmake/FindQAD.cmake index 68fbdb4..9347d97 100644 --- a/cmake/FindQAD.cmake +++ b/cmake/FindQAD.cmake @@ -11,6 +11,7 @@ Create imported targets: * QAD::SQLTable * QAD::TouchWidgets * QAD::Doc + * QAD::VirtualKeyboard * QAD::PIQt * QAD::PIQtUtils @@ -46,7 +47,7 @@ if(QAD_FIND_VERSION VERSION_GREATER QAD_VERSION) message(FATAL_ERROR "QAD version ${QAD_VERSION} is available, but ${QAD_FIND_VERSION} requested!") endif() -set(__libs "utils;widgets;application;blockview;graphic;graphic_analysis;graphic3d;sql;sql_table;touch_widgets;doc;map") +set(__libs "utils;widgets;application;blockview;graphic;graphic_analysis;graphic3d;sql;sql_table;touch_widgets;doc;map;virtual_keyboard") if (PIP_FOUND OR BUILDING_PIP) list(APPEND __libs "piqt;piqt_utils") endif() @@ -65,6 +66,7 @@ set(__module_doc Doc ) set(__module_map Map ) set(__module_piqt PIQt ) set(__module_piqt_utils PIQtUtils ) +set(__module_virtual_keyboard VirtualKeyboard) foreach (_l ${__libs}) set( __inc_${_l} "") @@ -82,6 +84,7 @@ set(__deps_sql_table "QAD::Widgets") set(__deps_map "QAD::Utils;QAD::PIQt") set(__deps_piqt "QAD::Widgets;PIP") set(__deps_piqt_utils "QAD::Blockview;QAD::PIQt") +set(__deps_virtual_keyboard "QAD::PIQt") #message("find QAD ${BUILDING_QAD}") diff --git a/icons/virtual_keyboard.png b/icons/virtual_keyboard.png new file mode 100644 index 0000000000000000000000000000000000000000..7c3e73b8c8cb2e5e6a46c456d8a8053fed7855e1 GIT binary patch literal 634 zcmV-=0)_pFP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D0uM<Z-h4W`-7aFW*zh%Pf8-4W0s%-Q5=0^q$mMdBN+mQJ4NN8zWV2bp(OW8&f*TPW z&7-x%(R!Io2B*`B>+9>cd*?~Fbe!yrnqTlbs z?WS}eh1uohC5uL*jFYTbEHbTD%TlQn^Lo917B_g25mvl?uY)Fm7*eVKSLSp3P2w;IOa|3z^`LNyW5y1L1NC|xX0sXN@mPpZhRfv=27^J2*=#nEA#wZt9*f07aKqtH zh*>NaaSkUL(G{p{-Mit$M-nN0qXI~Rp;+-|qA+wG3dd_ErrgMn!O z4s`829uMa8xp1yfD8yH+*K6TeWK35km&+j(3ZYys!)~{Wdwg?qBSg-DglS%@)k3G! zfljA`TCEnF5yBT{(f|Me07*qoM6N<$f)?i*m;e9( literal 0 HcmV?d00001 diff --git a/libs/virtual_keyboard/CMakeLists.txt b/libs/virtual_keyboard/CMakeLists.txt new file mode 100644 index 0000000..ba3090f --- /dev/null +++ b/libs/virtual_keyboard/CMakeLists.txt @@ -0,0 +1,4 @@ +find_package(PIP) +if (PIP_FOUND) + qad_library(virtual_keyboard "Gui;Widgets" "qad_piqt") +endif() diff --git a/libs/virtual_keyboard/default.json b/libs/virtual_keyboard/default.json new file mode 100644 index 0000000..0b838aa --- /dev/null +++ b/libs/virtual_keyboard/default.json @@ -0,0 +1,99 @@ +{ +"default_page": "latin", +"pages": [ + { + "name": "numeric", + "rows": [ + [ + {"role": "l", "text": "1"}, + {"role": "l", "text": "2"}, + {"role": "l", "text": "3"}, + {"role": "l", "text": "4"}, + {"role": "l", "text": "5"}, + {"role": "l", "text": "6"}, + {"role": "l", "text": "7"}, + {"role": "l", "text": "8"}, + {"role": "l", "text": "9"}, + {"role": "l", "text": "0"}, + {"role": "backspace"} + ], + [ + {"role": "l", "text": "@"}, + {"role": "l", "text": "#"}, + {"role": "l", "text": "%"}, + {"role": "l", "text": "&&"}, + {"role": "l", "text": "*"}, + {"role": "l", "text": "-"}, + {"role": "l", "text": "+"}, + {"role": "l", "text": "("}, + {"role": "l", "text": ")"}, + {"role": "return", "stretch": 2} + ], + [ + {"role": "l", "text": "!"}, + {"role": "l", "text": "\""}, + {"role": "l", "text": "<"}, + {"role": "l", "text": ">"}, + {"role": "l", "text": "'"}, + {"role": "l", "text": ":"}, + {"role": "l", "text": ";"}, + {"role": "l", "text": "/"}, + {"role": "l", "text": "?"}, + {"role": "l", "text": "_"} + ], + [ + {"role": "gotopage", "text": "en", "page": "latin"}, + {"role": "space", "stretch": 6}, + {"role": "hide"} + ] + ] + }, + { + "name": "latin", + "rows": [ + [ + {"role": "l", "text": "q"}, + {"role": "l", "text": "w"}, + {"role": "l", "text": "e"}, + {"role": "l", "text": "r"}, + {"role": "l", "text": "t"}, + {"role": "l", "text": "y"}, + {"role": "l", "text": "u"}, + {"role": "l", "text": "i"}, + {"role": "l", "text": "o"}, + {"role": "l", "text": "p"}, + {"role": "backspace"} + ], + [ + {"role": "l", "text": "a"}, + {"role": "l", "text": "s"}, + {"role": "l", "text": "d"}, + {"role": "l", "text": "f"}, + {"role": "l", "text": "g"}, + {"role": "l", "text": "h"}, + {"role": "l", "text": "j"}, + {"role": "l", "text": "k"}, + {"role": "l", "text": "l"}, + {"role": "return", "stretch": 2} + ], + [ + {"role": "l", "text": "z"}, + {"role": "l", "text": "x"}, + {"role": "l", "text": "c"}, + {"role": "l", "text": "v"}, + {"role": "l", "text": "b"}, + {"role": "l", "text": "n"}, + {"role": "l", "text": "m"}, + {"role": "shift", "stretch": 2} + ], + [ + {"role": "gotopage", "text": "123", "page": "numeric"}, + {"role": "l", "text": ","}, + {"role": "space", "stretch": 6}, + {"role": "l", "text": "."}, + {"role": "hide"} + ] + ] + } +] +} \ No newline at end of file diff --git a/libs/virtual_keyboard/plugin/CMakeLists.txt b/libs/virtual_keyboard/plugin/CMakeLists.txt new file mode 100644 index 0000000..ef20ecf --- /dev/null +++ b/libs/virtual_keyboard/plugin/CMakeLists.txt @@ -0,0 +1 @@ +qad_plugin(virtual_keyboard "Gui;Widgets" "") diff --git a/libs/virtual_keyboard/plugin/qad_virtual_keyboard.cpp b/libs/virtual_keyboard/plugin/qad_virtual_keyboard.cpp new file mode 100644 index 0000000..c7d80db --- /dev/null +++ b/libs/virtual_keyboard/plugin/qad_virtual_keyboard.cpp @@ -0,0 +1,17 @@ +#include "qad_virtual_keyboard.h" + +#include "virtual_keyboard_plugin.h" + +QADVirtualKeyboard::QADVirtualKeyboard(QObject * parent): QObject(parent) { + m_widgets << new VirtualKeyboardPlugin(this); +} + + +QList QADVirtualKeyboard::customWidgets() const { + return m_widgets; +} + + +#if QT_VERSION < 0x050000 +Q_EXPORT_PLUGIN2(qad_virtual_keyboard_plugin, QADVirtualKeyboard) +#endif diff --git a/libs/virtual_keyboard/plugin/qad_virtual_keyboard.h b/libs/virtual_keyboard/plugin/qad_virtual_keyboard.h new file mode 100644 index 0000000..6ae2327 --- /dev/null +++ b/libs/virtual_keyboard/plugin/qad_virtual_keyboard.h @@ -0,0 +1,24 @@ +#ifndef qad_virtual_keyboard_h +#define qad_virtual_keyboard_h + +#include +#include + +class QADVirtualKeyboard + : public QObject + , public QDesignerCustomWidgetCollectionInterface { + Q_OBJECT +#if QT_VERSION >= 0x050000 + Q_PLUGIN_METADATA(IID "qad.virtual_keyboard") +#endif + Q_INTERFACES(QDesignerCustomWidgetCollectionInterface) + +public: + explicit QADVirtualKeyboard(QObject * parent = 0); + virtual QList customWidgets() const; + +private: + QList m_widgets; +}; + +#endif diff --git a/libs/virtual_keyboard/plugin/virtual_keyboard_plugin.cpp b/libs/virtual_keyboard/plugin/virtual_keyboard_plugin.cpp new file mode 100644 index 0000000..711763b --- /dev/null +++ b/libs/virtual_keyboard/plugin/virtual_keyboard_plugin.cpp @@ -0,0 +1,62 @@ +#include "virtual_keyboard_plugin.h" + +#include "virtual_keyboard.h" + +#include +#include +#include + + +VirtualKeyboardPlugin::VirtualKeyboardPlugin(QObject * parent): QObject(parent) { + m_initialized = false; +} + +void VirtualKeyboardPlugin::initialize(QDesignerFormEditorInterface * /* core */) { + if (m_initialized) return; + + // Add extension registrations, etc. here + + m_initialized = true; +} + +bool VirtualKeyboardPlugin::isInitialized() const { + return m_initialized; +} + +QWidget * VirtualKeyboardPlugin::createWidget(QWidget * parent) { + auto ret = new VirtualKeyboard(parent); + ret->setAlwaysVisible(true); + return ret; +} + +QString VirtualKeyboardPlugin::name() const { + return QLatin1String("VirtualKeyboard"); +} + +QString VirtualKeyboardPlugin::group() const { + return QLatin1String("Touch Widgets"); +} + +QIcon VirtualKeyboardPlugin::icon() const { + return QIcon(":/icons/virtual_keyboard.png"); +} + +QString VirtualKeyboardPlugin::toolTip() const { + return QLatin1String(""); +} + +QString VirtualKeyboardPlugin::whatsThis() const { + return QLatin1String(""); +} + +bool VirtualKeyboardPlugin::isContainer() const { + return false; +} + +QString VirtualKeyboardPlugin::domXml() const { + return QLatin1String("\n\n"); +} + +QString VirtualKeyboardPlugin::includeFile() const { + return QLatin1String("virtual_keyboard.h"); +} diff --git a/libs/virtual_keyboard/plugin/virtual_keyboard_plugin.h b/libs/virtual_keyboard/plugin/virtual_keyboard_plugin.h new file mode 100644 index 0000000..5cc6306 --- /dev/null +++ b/libs/virtual_keyboard/plugin/virtual_keyboard_plugin.h @@ -0,0 +1,36 @@ +#ifndef virtual_keyboard_plugin_h +#define virtual_keyboard_plugin_h + +#include +#if QT_VERSION >= 0x050000 +# include +#else +# include +#endif + +class VirtualKeyboardPlugin + : public QObject + , public QDesignerCustomWidgetInterface { + Q_OBJECT + Q_INTERFACES(QDesignerCustomWidgetInterface) + +public: + VirtualKeyboardPlugin(QObject * parent = 0); + + bool isContainer() const; + bool isInitialized() const; + QIcon icon() const; + QString domXml() const; + QString group() const; + QString includeFile() const; + QString name() const; + QString toolTip() const; + QString whatsThis() const; + QWidget * createWidget(QWidget * parent); + void initialize(QDesignerFormEditorInterface * core); + +private: + bool m_initialized; +}; + +#endif diff --git a/libs/virtual_keyboard/virtual_keyboard.cpp b/libs/virtual_keyboard/virtual_keyboard.cpp new file mode 100644 index 0000000..48f54c6 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard.cpp @@ -0,0 +1,166 @@ +#include "virtual_keyboard.h" + +#include "ui_virtual_keyboard.h" +#include "virtual_keyboard_layout.h" +#include "virtual_keyboard_layout_page.h" + +#include +#include +#include +#include +#include +#include +#include +#include + + +VirtualKeyboard::VirtualKeyboard(QWidget * parent): QWidget(parent) { + ui = new Ui::VirtualKeyboard(); + ui->setupUi(this); + ui->stackedPages->setStyle(QStyleFactory::create("Fusion")); + connect(qApp, &QApplication::focusChanged, this, [this](QWidget * old, QWidget * now) { + if (watched) { + watched->removeEventFilter(this); + watched = nullptr; + } + if (!now || !layout) { + setKeyboardVisible(false); + return; + } + if (!now->inputMethodQuery(Qt::ImCursorPosition).isValid()) { + setKeyboardVisible(false); + return; + } + watched = now; + auto imh = Qt::InputMethodHints(watched->inputMethodQuery(Qt::ImHints).toInt()); + layout->setDefaultPage(); + setKeyboardVisible(true); + layout->applyHints(imh); + watched->installEventFilter(this); + }); +} + + +VirtualKeyboard::~VirtualKeyboard() { + if (watched) { + watched->removeEventFilter(this); + watched = nullptr; + } + destroy(); + delete ui; +} + + +void VirtualKeyboard::init(QString layout_path) { + destroy(); + QFile f(layout_path); + if (!f.open(QIODevice::ReadOnly)) return; + QJsonDocument doc = QJsonDocument::fromJson(f.readAll()); + layout = new VirtualKeyboardLayout(doc.object()); + auto pages = layout->getPages(); + for (auto p: pages) + ui->stackedPages->addWidget(p); + connect(layout, &VirtualKeyboardLayout::gotoPageRequest, this, [this](QString pagename) { + auto page = layout->getPageByName(pagename); + if (!page) return; + ui->stackedPages->setCurrentWidget(page); + layout->setCurrentPage(page); + }); + connect(layout, &VirtualKeyboardLayout::hideRequest, this, [this]() { setKeyboardVisible(false); }); + layout->setDefaultPage(); + // piCout << pages.size(); + adjust(); +} + + +bool VirtualKeyboard::eventFilter(QObject * o, QEvent * e) { + if (e->type() == QEvent::MouseButtonPress) setKeyboardVisible(true); + return QWidget::eventFilter(o, e); +} + + +void VirtualKeyboard::changeEvent(QEvent * e) { + QWidget::changeEvent(e); + switch (e->type()) { + case QEvent::LanguageChange: ui->retranslateUi(this); break; + case QEvent::Show: ui->retranslateUi(this); break; + default: break; + } +} + + +void VirtualKeyboard::resizeEvent(QResizeEvent * e) { + if (prev_width == width()) return; + prev_width = width(); + adjust(); +} + + +void VirtualKeyboard::showEvent(QShowEvent * e) { + if (first_show) { + first_show = false; + if (layout_path_.isEmpty()) init(":/virtual_keyboard/default.json"); + } + QWidget::showEvent(e); +} + + +void VirtualKeyboard::destroy() { + piDeleteSafety(layout); + while (ui->stackedPages->count() > 0) + delete ui->stackedPages->widget(0); +} + + +void VirtualKeyboard::adjust() { + if (!layout) return; + auto pages = layout->getPages(); + int cols = 0; + for (auto p: pages) + cols = piMaxi(cols, p->buttonColumns()); + if (cols < 1) return; + int spacing = style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, nullptr, this); + int pixel_width = width() - spacing * (cols - 1) - ui->verticalLayout->margin() * 2; + int bw = piMaxi(2, pixel_width / cols); + ui->stackedPages->setStyleSheet(QString("font:%1px").arg(bw / 2)); + for (auto p: pages) + p->setButtonsSize(bw); + // piCout << cols << spacing << bw; +} + + +void VirtualKeyboard::setKeyboardVisible(bool yes) { + if (always_visible || !isEnabled()) return; + if (yes) { + stopIndexedTimer(0); + setVisible(true); + } else startIndexedTimer(0, 10_Hz, [this]{ + if (QApplication::mouseButtons() != 0) return; + stopIndexedTimer(0); + setVisible(false); + }); +} + + +void VirtualKeyboard::setLayoutPath(const QString & path) { + layout_path_ = path; + init(layout_path_); +} + + +void VirtualKeyboard::setAlwaysVisible(bool yes) { + always_visible = yes; + if (yes) show(); +} + + +void VirtualKeyboard::setEnabled(bool yes) { + if (!yes) hide(); + QWidget::setEnabled(yes); +} + + +void VirtualKeyboard::setDisabled(bool yes) { + if (yes) hide(); + QWidget::setDisabled(yes); +} diff --git a/libs/virtual_keyboard/virtual_keyboard.h b/libs/virtual_keyboard/virtual_keyboard.h new file mode 100644 index 0000000..c65f530 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard.h @@ -0,0 +1,54 @@ +#ifndef virtual_keyboard_h +#define virtual_keyboard_h + +#include "qad_virtual_keyboard_export.h" + +#include +#include + +namespace Ui { +class VirtualKeyboard; +}; +class VirtualKeyboardLayout; + + +class QAD_VIRTUAL_KEYBOARD_EXPORT VirtualKeyboard: public QWidget, private IndexedTimer<> { + Q_OBJECT + Q_PROPERTY(QString layoutPath READ layoutPath WRITE setLayoutPath) + +public: + VirtualKeyboard(QWidget * parent = nullptr); + ~VirtualKeyboard(); + + const QString & layoutPath() const { return layout_path_; } + void setLayoutPath(const QString & path); + + bool isAlwaysVisible() const { return always_visible; } + void setAlwaysVisible(bool yes); + + void setEnabled(bool yes); + void setDisabled(bool yes); + +protected: + bool eventFilter(QObject * o, QEvent * e) override; + void changeEvent(QEvent * e) override; + void resizeEvent(QResizeEvent * e) override; + void showEvent(QShowEvent * e) override; + + void init(QString layout_path); + void destroy(); + void adjust(); + void setKeyboardVisible(bool yes); + +private: + Ui::VirtualKeyboard * ui = nullptr; + VirtualKeyboardLayout * layout = nullptr; + QWidget * watched = nullptr; + QString layout_path_; + bool first_show = true, always_visible = false; + int prev_width = -1; + +private slots: +}; + +#endif diff --git a/libs/virtual_keyboard/virtual_keyboard.qrc b/libs/virtual_keyboard/virtual_keyboard.qrc new file mode 100644 index 0000000..8e68908 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard.qrc @@ -0,0 +1,8 @@ + + + ../../icons/virtual_keyboard.png + + + default.json + + diff --git a/libs/virtual_keyboard/virtual_keyboard.ui b/libs/virtual_keyboard/virtual_keyboard.ui new file mode 100644 index 0000000..fcbf597 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard.ui @@ -0,0 +1,74 @@ + + + VirtualKeyboard + + + + 0 + 0 + 565 + 389 + + + + + 0 + 0 + + + + QWidget{ +background-color: black; +} +QToolButton{ +background-color: rgb(64,64,64); +color: white; +} +QToolButton:pressed{ +background-color: rgb(48,48,48); +} +QToolButton:disabled{ +background-color: rgb(32,32,32); +color: rgb(128,128,128); +} +QToolButton:checked{ +background-color: rgb(52,52,52); +color: rgb(92,192,92); +} + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + font:14pt + + + + + + + + + diff --git a/libs/virtual_keyboard/virtual_keyboard_layout.cpp b/libs/virtual_keyboard/virtual_keyboard_layout.cpp new file mode 100644 index 0000000..b781ed1 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard_layout.cpp @@ -0,0 +1,72 @@ +#include "virtual_keyboard_layout.h" + +#include +#include + + +VirtualKeyboardLayout::VirtualKeyboardLayout(QJsonObject root) { + default_page = root["default_page"].toString(); + auto pa = root["pages"].toArray(); + for (auto p: pa) { + auto page = new VirtualKeyboardLayoutPage(p.toObject()); + if (page->isValid()) { + connect(page, &VirtualKeyboardLayoutPage::gotoPageRequest, this, &VirtualKeyboardLayout::gotoPageRequest); + connect(page, &VirtualKeyboardLayoutPage::hideRequest, this, &VirtualKeyboardLayout::hideRequest); + pages << page; + } else + delete page; + } +} + + +VirtualKeyboardLayout::~VirtualKeyboardLayout() { + // piDeleteAllAndClear(pages); +} + + +VirtualKeyboardLayoutPage * VirtualKeyboardLayout::getPageByName(QString name) const { + for (auto p: pages) + if (p->name() == name) return p; + return nullptr; +} + + +void VirtualKeyboardLayout::setDefaultPage() { + emit gotoPageRequest(default_page); +} + + +void VirtualKeyboardLayout::setCurrentPage(VirtualKeyboardLayoutPage * p) { + current_page = p; +} + + +void VirtualKeyboardLayout::applyHints(Qt::InputMethodHints hints) { + // qDebug() << hints << current_page; + if (hints.testFlag(Qt::ImhPreferNumbers) || hints.testFlag(Qt::ImhDigitsOnly)) gotoPageRequest("numeric"); + if (hints.testFlag(Qt::ImhPreferLatin) || hints.testFlag(Qt::ImhLatinOnly)) gotoPageRequest("latin"); + if (hints.testFlag(Qt::ImhPreferLowercase) || hints.testFlag(Qt::ImhLowercaseOnly) || hints.testFlag(Qt::ImhNoAutoUppercase)) + setCapital(false); + else + setCapital(true); + setGotoEnabled(!hints.testFlag(Qt::ImhDigitsOnly) && !hints.testFlag(Qt::ImhLatinOnly)); + setCapitalEnabled(!hints.testFlag(Qt::ImhUppercaseOnly) && !hints.testFlag(Qt::ImhLowercaseOnly)); +} + + +void VirtualKeyboardLayout::setGotoEnabled(bool yes) { + for (auto p: pages) + p->setGotoEnabled(yes); +} + + +void VirtualKeyboardLayout::setCapitalEnabled(bool yes) { + for (auto p: pages) + p->setCapitalEnabled(yes); +} + + +void VirtualKeyboardLayout::setCapital(bool yes) { + for (auto p: pages) + p->setCapital(yes); +} diff --git a/libs/virtual_keyboard/virtual_keyboard_layout.h b/libs/virtual_keyboard/virtual_keyboard_layout.h new file mode 100644 index 0000000..a53a1aa --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard_layout.h @@ -0,0 +1,39 @@ +#ifndef virtual_keyboard_layout_h +#define virtual_keyboard_layout_h + +#include +#include "virtual_keyboard_layout_page.h" + + +class VirtualKeyboardLayout: public QObject { + Q_OBJECT + +public: + VirtualKeyboardLayout(QJsonObject root); + ~VirtualKeyboardLayout(); + + QVector getPages() const {return pages;} + VirtualKeyboardLayoutPage * getPageByName(QString name) const; + + void setDefaultPage(); + void setCurrentPage(VirtualKeyboardLayoutPage * p); + void applyHints(Qt::InputMethodHints hints); + +protected: + void setGotoEnabled(bool yes); + void setCapitalEnabled(bool yes); + void setCapital(bool yes); + + QVector pages; + VirtualKeyboardLayoutPage * current_page = nullptr; + QString default_page; + +private slots: + +signals: + void gotoPageRequest(QString); + void hideRequest(); + +}; + +#endif diff --git a/libs/virtual_keyboard/virtual_keyboard_layout_page.cpp b/libs/virtual_keyboard/virtual_keyboard_layout_page.cpp new file mode 100644 index 0000000..c594c45 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard_layout_page.cpp @@ -0,0 +1,154 @@ +#include "virtual_keyboard_layout_page.h" + +#include +#include +#include +#include +#include +#include + +static const char property_role[] = "role"; +static const char property_page[] = "page"; +static const char property_stretch[] = "stretch"; + + +VirtualKeyboardLayoutPage::VirtualKeyboardLayoutPage(QJsonObject root) { + auto main_lay = new QBoxLayout(QBoxLayout::TopToBottom); + name_ = root["name"].toString(); + auto rows = root["rows"].toArray(); + for (auto row: rows) { + auto rowa = row.toArray(); + QBoxLayout * row_lay = nullptr; + int cur_cols = 0; + for (auto rb: rowa) { + auto * b = createButton(rb.toObject()); + if (!b) continue; + if (!row_lay) row_lay = new QBoxLayout(QBoxLayout::LeftToRight); + buttons << b; + row_lay->addWidget(b, b->property(property_stretch).toInt()); + ++cur_cols; + } + if (row_lay) main_lay->addLayout(row_lay); + columns = piMaxi(columns, cur_cols); + } + // qDebug() << "VirtualKeyboardLayoutPage" << name; + setLayout(main_lay); +} + + +VirtualKeyboardLayoutPage::~VirtualKeyboardLayoutPage() {} + + +void VirtualKeyboardLayoutPage::setCapital(bool yes) { + capital = yes; + for (auto b: buttons) { + auto role = buttonRole(b); + if (role == rLetter) { + b->setText(yes ? b->text().toUpper() : b->text().toLower()); + } + if (role == rShift) { + // b->setText(yes ? QString::fromUtf8("⇫") : QString::fromUtf8("⇧")); + b->setChecked(yes); + } + } +} + + +void VirtualKeyboardLayoutPage::setGotoEnabled(bool yes) { + for (auto b: buttons) { + auto role = buttonRole(b); + if (role == rGotoPage) b->setEnabled(yes); + } +} + + +void VirtualKeyboardLayoutPage::setCapitalEnabled(bool yes) { + capital_enabled = yes; + for (auto b: buttons) { + auto role = buttonRole(b); + if (role == rShift) b->setEnabled(yes); + } +} + + +void VirtualKeyboardLayoutPage::setButtonsSize(int h) { + for (auto b: buttons) { + b->setFixedHeight(h); + } + if (!isVisible()) { + adjustSize(); + layout()->invalidate(); + } +} + + +QToolButton * VirtualKeyboardLayoutPage::createButton(QJsonObject jb) { + QString role_str = jb["role"].toString(); + if (role_str.isEmpty()) return nullptr; + auto ret = new QToolButton(); + int role = roleFromText(role_str); + int stretch = jb["stretch"].toInt(); + if (stretch == 0) stretch = 1; + auto text = jb["text"].toString(); + ret->setProperty(property_role, role); + ret->setProperty(property_stretch, stretch); + ret->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); + ret->setFocusPolicy(Qt::NoFocus); + ret->setMinimumSize(QSize(2, 2)); + ret->setAutoRepeat(true); + switch (role) { + case rBackspace: text = QString::fromUtf8("⌫"); break; + case rReturn: text = QString::fromUtf8("⏎"); break; + case rSpace: text = QString::fromUtf8("────"); break; + case rShift: + text = QString::fromUtf8("⤊"); + ret->setCheckable(true); + break; // ⇪⇧⇫⇯⇩⬆⌨ + case rHide: text = QString::fromUtf8("⨂"); break; + case rGotoPage: ret->setProperty(property_page, jb["page"].toString()); break; + } + ret->setText(text); + connect(ret, &QToolButton::clicked, this, [this, role, ret](bool press) { + int key = 0; + QString text; + switch (role) { + case rLetter: + text = ret->text(); + if (text.startsWith('&')) text.remove(0, 1); + break; + case rBackspace: key = Qt::Key_Backspace; break; + case rReturn: key = Qt::Key_Return; break; + case rSpace: + key = Qt::Key_Space; + text = " "; + break; + case rHide: emit hideRequest(); break; + case rShift: setCapital(!isCapital()); break; + case rGotoPage: emit gotoPageRequest(ret->property(property_page).toString()); break; + } + if (key == 0 && text.isEmpty()) return; + QApplication::postEvent(QApplication::focusWidget(), new QKeyEvent(QEvent::KeyPress, key, Qt::NoModifier, text)); + QApplication::postEvent(QApplication::focusWidget(), new QKeyEvent(QEvent::KeyRelease, key, Qt::NoModifier, text)); + if (role == rLetter && isCapital() && capital_enabled) setCapital(false); + }); + return ret; +} + + +VirtualKeyboardLayoutPage::Role VirtualKeyboardLayoutPage::roleFromText(QString str) { + str = str.toLower(); + if (str == "l") return rLetter; + if (str == "backspace") return rBackspace; + if (str == "return") return rReturn; + if (str == "space") return rSpace; + if (str == "hide") return rHide; + if (str == "shift") return rShift; + if (str == "gotopage") return rGotoPage; + return rInvalid; +} + + +VirtualKeyboardLayoutPage::Role VirtualKeyboardLayoutPage::buttonRole(QToolButton * b) const { + if (!b) return rInvalid; + return static_cast(b->property(property_role).toInt()); +} diff --git a/libs/virtual_keyboard/virtual_keyboard_layout_page.h b/libs/virtual_keyboard/virtual_keyboard_layout_page.h new file mode 100644 index 0000000..e4a85c1 --- /dev/null +++ b/libs/virtual_keyboard/virtual_keyboard_layout_page.h @@ -0,0 +1,55 @@ +#ifndef virtual_keyboard_layout_page_h +#define virtual_keyboard_layout_page_h + +#include +#include + + +class VirtualKeyboardLayoutPage: public QWidget { + Q_OBJECT + +public: + VirtualKeyboardLayoutPage(QJsonObject root); + ~VirtualKeyboardLayoutPage(); + + bool isValid() const {return !buttons.isEmpty();} + QString name() const {return name_;} + + bool isCapital() const {return capital;} + void setCapital(bool yes); + void setGotoEnabled(bool yes); + void setCapitalEnabled(bool yes); + + int buttonColumns() const {return columns;} + void setButtonsSize(int h); + + enum Role { + rInvalid, + rLetter, + rBackspace, + rReturn, + rSpace, + rHide, + rShift, + rGotoPage, + }; + +protected: + QToolButton * createButton(QJsonObject jb); + Role roleFromText(QString str); + Role buttonRole(QToolButton * b) const; + + QVector buttons; + QString name_; + int columns = 0; + bool capital = false, capital_enabled = true; + +private slots: + +signals: + void gotoPageRequest(QString); + void hideRequest(); + +}; + +#endif