tablette/src/view/virtualKeyboard.cpp
2025-09-02 15:25:37 +02:00

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();
}