558 lines
16 KiB
C++
558 lines
16 KiB
C++
#include "virtualKeyboard.hpp"
|
|
#include <QLabel>
|
|
#include <QFocusEvent>
|
|
#include <QShowEvent>
|
|
#include <QHideEvent>
|
|
#include <QResizeEvent>
|
|
#include <QScreen>
|
|
#include <QPalette>
|
|
#include <QPainter>
|
|
#include <QPaintEvent>
|
|
|
|
VirtualKeyboard::VirtualKeyboard(QWidget* parent)
|
|
: QWidget(parent)
|
|
, m_mainLayout(nullptr)
|
|
, m_keyboardLayout(nullptr)
|
|
, m_functionLayout(nullptr)
|
|
, m_displayEdit(nullptr)
|
|
, m_targetWidget(nullptr)
|
|
, m_capsLock(false)
|
|
, m_shift(false)
|
|
, m_spaceButton(nullptr)
|
|
, m_backspaceButton(nullptr)
|
|
, m_enterButton(nullptr)
|
|
, m_shiftButton(nullptr)
|
|
, m_capsLockButton(nullptr)
|
|
, m_hideButton(nullptr)
|
|
{
|
|
// Ne pas créer de fenêtre séparée, utiliser le parent
|
|
setAttribute(Qt::WA_ShowWithoutActivating);
|
|
setAutoFillBackground(true); // Permettre le fond personnalisé
|
|
|
|
// Initialize key layouts
|
|
m_qwertyLayout << "q" << "w" << "e" << "r" << "t" << "y" << "u" << "i" << "o" << "p"
|
|
<< "a" << "s" << "d" << "f" << "g" << "h" << "j" << "k" << "l"
|
|
<< "z" << "x" << "c" << "v" << "b" << "n" << "m";
|
|
|
|
m_numericLayout << "1" << "2" << "3" << "4" << "5" << "6" << "7" << "8" << "9" << "0";
|
|
|
|
setupKeyboard();
|
|
applyKeyboardStyling();
|
|
updateKeyLabels();
|
|
}
|
|
|
|
VirtualKeyboard::~VirtualKeyboard()
|
|
{
|
|
// Qt handles cleanup automatically
|
|
}
|
|
|
|
void VirtualKeyboard::setTargetWidget(QWidget* target)
|
|
{
|
|
m_targetWidget = target;
|
|
}
|
|
|
|
void VirtualKeyboard::showForWidget(QWidget* target)
|
|
{
|
|
setTargetWidget(target);
|
|
|
|
// Initialiser la zone d'affichage avec le contenu actuel
|
|
if (QLineEdit* lineEdit = qobject_cast<QLineEdit*>(target)) {
|
|
m_displayEdit->setText(lineEdit->text());
|
|
} else if (QTextEdit* textEdit = qobject_cast<QTextEdit*>(target)) {
|
|
m_displayEdit->setText(textEdit->toPlainText());
|
|
} else {
|
|
m_displayEdit->clear();
|
|
}
|
|
|
|
// Positionner le clavier comme overlay sur l'application parent
|
|
if (parentWidget()) {
|
|
setGeometry(parentWidget()->rect());
|
|
}
|
|
|
|
show();
|
|
raise();
|
|
}
|
|
|
|
void VirtualKeyboard::hideKeyboard()
|
|
{
|
|
hide();
|
|
m_targetWidget = nullptr;
|
|
m_displayEdit->clear();
|
|
}
|
|
|
|
void VirtualKeyboard::setupKeyboard()
|
|
{
|
|
m_mainLayout = new QVBoxLayout(this);
|
|
m_mainLayout->setSpacing(20);
|
|
m_mainLayout->setContentsMargins(50, 50, 50, 50);
|
|
|
|
// Header with title and hide button
|
|
QHBoxLayout* headerLayout = new QHBoxLayout();
|
|
QLabel* titleLabel = new QLabel("Clavier Virtuel");
|
|
titleLabel->setObjectName("keyboardTitle");
|
|
|
|
m_hideButton = new QPushButton("✕ Fermer");
|
|
m_hideButton->setObjectName("hideButton");
|
|
m_hideButton->setFixedSize(180, 70);
|
|
connect(m_hideButton, &QPushButton::clicked, this, &VirtualKeyboard::hideKeyboard);
|
|
|
|
headerLayout->addWidget(titleLabel);
|
|
headerLayout->addStretch();
|
|
headerLayout->addWidget(m_hideButton);
|
|
|
|
m_mainLayout->addLayout(headerLayout);
|
|
|
|
// Zone d'affichage du texte en cours de saisie
|
|
m_displayEdit = new QLineEdit();
|
|
m_displayEdit->setObjectName("displayEdit");
|
|
m_displayEdit->setReadOnly(true);
|
|
m_displayEdit->setFixedHeight(80);
|
|
m_displayEdit->setPlaceholderText("Le texte saisi apparaîtra ici...");
|
|
m_mainLayout->addWidget(m_displayEdit);
|
|
|
|
// Add spacer to center keyboard
|
|
m_mainLayout->addStretch(1);
|
|
|
|
// Numeric row
|
|
setupNumericKeys();
|
|
|
|
// Alphabetic rows
|
|
setupAlphabeticKeys();
|
|
|
|
// Function keys row
|
|
setupFunctionKeys();
|
|
|
|
// Add spacer to center keyboard
|
|
m_mainLayout->addStretch(1);
|
|
}
|
|
|
|
void VirtualKeyboard::setupNumericKeys()
|
|
{
|
|
QHBoxLayout* numericLayout = new QHBoxLayout();
|
|
numericLayout->setSpacing(15);
|
|
numericLayout->addStretch(); // Centrer les touches
|
|
|
|
for (const QString& key : m_numericLayout) {
|
|
QPushButton* button = new QPushButton(key);
|
|
button->setObjectName("numericKey");
|
|
button->setFixedSize(60, 60);
|
|
connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed);
|
|
m_numericKeys.append(button);
|
|
numericLayout->addWidget(button);
|
|
}
|
|
|
|
// Add special numeric characters
|
|
QStringList specialChars = {"-", "=", "[", "]", "\\"};
|
|
for (const QString& key : specialChars) {
|
|
QPushButton* button = new QPushButton(key);
|
|
button->setObjectName("specialKey");
|
|
button->setFixedSize(60, 60);
|
|
connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed);
|
|
numericLayout->addWidget(button);
|
|
}
|
|
|
|
numericLayout->addStretch(); // Centrer les touches
|
|
m_mainLayout->addLayout(numericLayout);
|
|
}
|
|
|
|
void VirtualKeyboard::setupAlphabeticKeys()
|
|
{
|
|
// First row: QWERTYUIOP
|
|
QHBoxLayout* firstRow = new QHBoxLayout();
|
|
firstRow->setSpacing(15);
|
|
firstRow->addStretch();
|
|
|
|
for (int i = 0; i < 10; ++i) {
|
|
QPushButton* button = new QPushButton(m_qwertyLayout[i]);
|
|
button->setObjectName("alphabeticKey");
|
|
button->setFixedSize(60, 60);
|
|
connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed);
|
|
m_alphabeticKeys.append(button);
|
|
firstRow->addWidget(button);
|
|
}
|
|
|
|
firstRow->addStretch();
|
|
m_mainLayout->addLayout(firstRow);
|
|
|
|
// Second row: ASDFGHJKL
|
|
QHBoxLayout* secondRow = new QHBoxLayout();
|
|
secondRow->setSpacing(15);
|
|
secondRow->addStretch();
|
|
|
|
for (int i = 10; i < 19; ++i) {
|
|
QPushButton* button = new QPushButton(m_qwertyLayout[i]);
|
|
button->setObjectName("alphabeticKey");
|
|
button->setFixedSize(60, 60);
|
|
connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed);
|
|
m_alphabeticKeys.append(button);
|
|
secondRow->addWidget(button);
|
|
}
|
|
|
|
secondRow->addStretch();
|
|
m_mainLayout->addLayout(secondRow);
|
|
|
|
// Third row: ZXCVBNM
|
|
QHBoxLayout* thirdRow = new QHBoxLayout();
|
|
thirdRow->setSpacing(15);
|
|
thirdRow->addStretch();
|
|
|
|
// Shift button
|
|
m_shiftButton = new QPushButton("⇧");
|
|
m_shiftButton->setObjectName("functionKey");
|
|
m_shiftButton->setFixedSize(80, 60);
|
|
m_shiftButton->setCheckable(true);
|
|
connect(m_shiftButton, &QPushButton::clicked, this, &VirtualKeyboard::onShiftPressed);
|
|
thirdRow->addWidget(m_shiftButton);
|
|
|
|
for (int i = 19; i < 26; ++i) {
|
|
QPushButton* button = new QPushButton(m_qwertyLayout[i]);
|
|
button->setObjectName("alphabeticKey");
|
|
button->setFixedSize(60, 60);
|
|
connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed);
|
|
m_alphabeticKeys.append(button);
|
|
thirdRow->addWidget(button);
|
|
}
|
|
|
|
// Backspace button
|
|
m_backspaceButton = new QPushButton("⌫");
|
|
m_backspaceButton->setObjectName("functionKey");
|
|
m_backspaceButton->setFixedSize(80, 60);
|
|
connect(m_backspaceButton, &QPushButton::clicked, this, &VirtualKeyboard::onBackspacePressed);
|
|
thirdRow->addWidget(m_backspaceButton);
|
|
|
|
thirdRow->addStretch();
|
|
m_mainLayout->addLayout(thirdRow);
|
|
}
|
|
|
|
void VirtualKeyboard::setupFunctionKeys()
|
|
{
|
|
m_functionLayout = new QHBoxLayout();
|
|
m_functionLayout->setSpacing(20);
|
|
m_functionLayout->addStretch();
|
|
|
|
// Caps Lock
|
|
m_capsLockButton = new QPushButton("Caps");
|
|
m_capsLockButton->setObjectName("functionKey");
|
|
m_capsLockButton->setFixedSize(80, 60);
|
|
m_capsLockButton->setCheckable(true);
|
|
connect(m_capsLockButton, &QPushButton::clicked, this, &VirtualKeyboard::onCapsLockPressed);
|
|
m_functionLayout->addWidget(m_capsLockButton);
|
|
|
|
// Space bar
|
|
m_spaceButton = new QPushButton("Espace");
|
|
m_spaceButton->setObjectName("spaceKey");
|
|
m_spaceButton->setFixedSize(240, 60);
|
|
connect(m_spaceButton, &QPushButton::clicked, this, &VirtualKeyboard::onSpacePressed);
|
|
m_functionLayout->addWidget(m_spaceButton);
|
|
|
|
// Enter
|
|
m_enterButton = new QPushButton("↵");
|
|
m_enterButton->setObjectName("functionKey");
|
|
m_enterButton->setFixedSize(80, 60);
|
|
connect(m_enterButton, &QPushButton::clicked, this, &VirtualKeyboard::onEnterPressed);
|
|
m_functionLayout->addWidget(m_enterButton);
|
|
|
|
// Common punctuation
|
|
QStringList punctuation = {".", ",", "?", "!", ":", ";"};
|
|
for (const QString& key : punctuation) {
|
|
QPushButton* button = new QPushButton(key);
|
|
button->setObjectName("punctuationKey");
|
|
button->setFixedSize(60, 60);
|
|
connect(button, &QPushButton::clicked, this, &VirtualKeyboard::onKeyPressed);
|
|
m_functionLayout->addWidget(button);
|
|
}
|
|
|
|
m_functionLayout->addStretch();
|
|
m_mainLayout->addLayout(m_functionLayout);
|
|
}
|
|
|
|
void VirtualKeyboard::applyKeyboardStyling()
|
|
{
|
|
// Utiliser une palette pour le fond au lieu du stylesheet
|
|
QPalette palette = this->palette();
|
|
palette.setColor(QPalette::Window, QColor(44, 62, 80, 250));
|
|
setPalette(palette);
|
|
|
|
setStyleSheet(R"(
|
|
|
|
#keyboardTitle {
|
|
color: #ecf0f1;
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
padding: 15px;
|
|
}
|
|
|
|
#displayEdit {
|
|
background-color: #34495e;
|
|
color: #ecf0f1;
|
|
border: 2px solid #5d6d7e;
|
|
border-radius: 12px;
|
|
font-size: 22px;
|
|
padding: 15px;
|
|
font-family: monospace;
|
|
}
|
|
|
|
#hideButton {
|
|
background-color: #e74c3c;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 25px;
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#hideButton:hover {
|
|
background-color: #c0392b;
|
|
}
|
|
|
|
#hideButton:pressed {
|
|
background-color: #a93226;
|
|
}
|
|
|
|
#alphabeticKey, #numericKey {
|
|
background-color: #34495e;
|
|
color: #ecf0f1;
|
|
border: 2px solid #5d6d7e;
|
|
border-radius: 12px;
|
|
font-size: 24px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#alphabeticKey:hover, #numericKey:hover {
|
|
background-color: #4a5f7a;
|
|
}
|
|
|
|
#alphabeticKey:pressed, #numericKey:pressed {
|
|
background-color: #3498db;
|
|
}
|
|
|
|
#functionKey {
|
|
background-color: #e67e22;
|
|
color: white;
|
|
border: 2px solid #d68910;
|
|
border-radius: 12px;
|
|
font-size: 18px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#functionKey:hover {
|
|
background-color: #f39c12;
|
|
}
|
|
|
|
#functionKey:pressed, #functionKey:checked {
|
|
background-color: #d35400;
|
|
}
|
|
|
|
#spaceKey {
|
|
background-color: #27ae60;
|
|
color: white;
|
|
border: 2px solid #229954;
|
|
border-radius: 12px;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#spaceKey:hover {
|
|
background-color: #2ecc71;
|
|
}
|
|
|
|
#spaceKey:pressed {
|
|
background-color: #1e8449;
|
|
}
|
|
|
|
#specialKey, #punctuationKey {
|
|
background-color: #8e44ad;
|
|
color: white;
|
|
border: 2px solid #7d3c98;
|
|
border-radius: 12px;
|
|
font-size: 20px;
|
|
font-weight: bold;
|
|
}
|
|
|
|
#specialKey:hover, #punctuationKey:hover {
|
|
background-color: #a569bd;
|
|
}
|
|
|
|
#specialKey:pressed, #punctuationKey:pressed {
|
|
background-color: #6c3483;
|
|
}
|
|
)");
|
|
}
|
|
|
|
void VirtualKeyboard::updateKeyLabels()
|
|
{
|
|
bool upperCase = m_capsLock || m_shift;
|
|
|
|
for (int i = 0; i < m_alphabeticKeys.size(); ++i) {
|
|
QString key = m_qwertyLayout[i];
|
|
if (upperCase) {
|
|
key = key.toUpper();
|
|
}
|
|
m_alphabeticKeys[i]->setText(key);
|
|
}
|
|
|
|
// Update shift button appearance
|
|
m_shiftButton->setChecked(m_shift);
|
|
m_capsLockButton->setChecked(m_capsLock);
|
|
}
|
|
|
|
void VirtualKeyboard::insertTextToTarget(const QString& text)
|
|
{
|
|
if (!m_targetWidget) return;
|
|
|
|
if (QLineEdit* lineEdit = qobject_cast<QLineEdit*>(m_targetWidget)) {
|
|
lineEdit->insert(text);
|
|
// Mettre à jour la zone d'affichage
|
|
m_displayEdit->setText(lineEdit->text());
|
|
} else if (QTextEdit* textEdit = qobject_cast<QTextEdit*>(m_targetWidget)) {
|
|
textEdit->insertPlainText(text);
|
|
// Mettre à jour la zone d'affichage
|
|
m_displayEdit->setText(textEdit->toPlainText());
|
|
}
|
|
}
|
|
|
|
void VirtualKeyboard::positionKeyboard()
|
|
{
|
|
if (!m_targetWidget) return;
|
|
|
|
// Get screen geometry
|
|
QScreen* screen = QApplication::primaryScreen();
|
|
QRect screenGeometry = screen->availableGeometry();
|
|
|
|
// Get target widget global position
|
|
QPoint targetPos = m_targetWidget->mapToGlobal(QPoint(0, 0));
|
|
QSize targetSize = m_targetWidget->size();
|
|
|
|
// Calculate keyboard size
|
|
adjustSize();
|
|
QSize keyboardSize = size();
|
|
|
|
// Position keyboard below the target widget
|
|
int x = targetPos.x();
|
|
int y = targetPos.y() + targetSize.height() + 10;
|
|
|
|
// Ensure keyboard stays within screen bounds
|
|
if (x + keyboardSize.width() > screenGeometry.right()) {
|
|
x = screenGeometry.right() - keyboardSize.width();
|
|
}
|
|
if (x < screenGeometry.left()) {
|
|
x = screenGeometry.left();
|
|
}
|
|
|
|
if (y + keyboardSize.height() > screenGeometry.bottom()) {
|
|
// If no space below, position above the target widget
|
|
y = targetPos.y() - keyboardSize.height() - 10;
|
|
}
|
|
|
|
move(x, y);
|
|
}
|
|
|
|
void VirtualKeyboard::showEvent(QShowEvent* event)
|
|
{
|
|
QWidget::showEvent(event);
|
|
// Le clavier est maintenant en plein écran, pas besoin de positionner
|
|
}
|
|
|
|
void VirtualKeyboard::hideEvent(QHideEvent* event)
|
|
{
|
|
QWidget::hideEvent(event);
|
|
m_targetWidget = nullptr;
|
|
}
|
|
|
|
void VirtualKeyboard::resizeEvent(QResizeEvent* event)
|
|
{
|
|
QWidget::resizeEvent(event);
|
|
// S'assurer que le clavier couvre toute la fenêtre parent si visible
|
|
if (isVisible() && parentWidget()) {
|
|
setGeometry(parentWidget()->rect());
|
|
}
|
|
}
|
|
|
|
void VirtualKeyboard::paintEvent(QPaintEvent* event)
|
|
{
|
|
QPainter painter(this);
|
|
painter.setRenderHint(QPainter::Antialiasing);
|
|
|
|
// Dessiner le fond avec transparence
|
|
QColor backgroundColor(44, 62, 80, 250);
|
|
painter.fillRect(rect(), backgroundColor);
|
|
|
|
// Dessiner la bordure
|
|
QPen borderPen(QColor(52, 73, 94), 3);
|
|
painter.setPen(borderPen);
|
|
painter.drawRoundedRect(rect().adjusted(1, 1, -1, -1), 15, 15);
|
|
|
|
QWidget::paintEvent(event);
|
|
}
|
|
|
|
// Slot implementations
|
|
void VirtualKeyboard::onKeyPressed()
|
|
{
|
|
QPushButton* button = qobject_cast<QPushButton*>(sender());
|
|
if (!button) return;
|
|
|
|
QString text = button->text();
|
|
insertTextToTarget(text);
|
|
|
|
// Reset shift after key press (but not caps lock)
|
|
if (m_shift && !m_capsLock) {
|
|
m_shift = false;
|
|
updateKeyLabels();
|
|
}
|
|
}
|
|
|
|
void VirtualKeyboard::onBackspacePressed()
|
|
{
|
|
if (!m_targetWidget) return;
|
|
|
|
if (QLineEdit* lineEdit = qobject_cast<QLineEdit*>(m_targetWidget)) {
|
|
lineEdit->backspace();
|
|
// Mettre à jour la zone d'affichage
|
|
m_displayEdit->setText(lineEdit->text());
|
|
} else if (QTextEdit* textEdit = qobject_cast<QTextEdit*>(m_targetWidget)) {
|
|
QTextCursor cursor = textEdit->textCursor();
|
|
cursor.deletePreviousChar();
|
|
textEdit->setTextCursor(cursor);
|
|
// Mettre à jour la zone d'affichage
|
|
m_displayEdit->setText(textEdit->toPlainText());
|
|
}
|
|
}
|
|
|
|
void VirtualKeyboard::onSpacePressed()
|
|
{
|
|
insertTextToTarget(" ");
|
|
|
|
// Reset shift after space (but not caps lock)
|
|
if (m_shift && !m_capsLock) {
|
|
m_shift = false;
|
|
updateKeyLabels();
|
|
}
|
|
}
|
|
|
|
void VirtualKeyboard::onEnterPressed()
|
|
{
|
|
insertTextToTarget("\n");
|
|
|
|
// Reset shift after enter (but not caps lock)
|
|
if (m_shift && !m_capsLock) {
|
|
m_shift = false;
|
|
updateKeyLabels();
|
|
}
|
|
}
|
|
|
|
void VirtualKeyboard::onShiftPressed()
|
|
{
|
|
m_shift = !m_shift;
|
|
updateKeyLabels();
|
|
}
|
|
|
|
void VirtualKeyboard::onCapsLockPressed()
|
|
{
|
|
m_capsLock = !m_capsLock;
|
|
// If caps lock is on, turn off shift
|
|
if (m_capsLock) {
|
|
m_shift = false;
|
|
}
|
|
updateKeyLabels();
|
|
}
|