1 // Copyright 2018 Citra Emulator Project
2 // Licensed under GPLv2 or any later version
3 // Refer to the license.txt file included.
4 
5 #include <QDialogButtonBox>
6 #include <QLabel>
7 #include <QLineEdit>
8 #include <QMessageBox>
9 #include <QString>
10 #include <QVBoxLayout>
11 #include "citra_qt/applets/swkbd.h"
12 
QtKeyboardValidator(QtKeyboard * keyboard_)13 QtKeyboardValidator::QtKeyboardValidator(QtKeyboard* keyboard_) : keyboard(keyboard_) {}
14 
validate(QString & input,int & pos) const15 QtKeyboardValidator::State QtKeyboardValidator::validate(QString& input, int& pos) const {
16     if (keyboard->ValidateFilters(input.toStdString()) == Frontend::ValidationError::None) {
17         if (input.size() > keyboard->config.max_text_length)
18             return State::Invalid;
19         return State::Acceptable;
20     } else {
21         return State::Invalid;
22     }
23 }
24 
QtKeyboardDialog(QWidget * parent,QtKeyboard * keyboard_)25 QtKeyboardDialog::QtKeyboardDialog(QWidget* parent, QtKeyboard* keyboard_)
26     : QDialog(parent), keyboard(keyboard_) {
27     using namespace Frontend;
28     const auto config = keyboard->config;
29     layout = new QVBoxLayout;
30     label = new QLabel(QString::fromStdString(config.hint_text));
31     line_edit = new QLineEdit;
32     line_edit->setValidator(new QtKeyboardValidator(keyboard));
33     buttons = new QDialogButtonBox;
34     // Initialize buttons
35     switch (config.button_config) {
36     case ButtonConfig::Triple:
37         buttons->addButton(config.button_text[1].empty()
38                                ? tr(SWKBD_BUTTON_FORGOT)
39                                : QString::fromStdString(config.button_text[1]),
40                            QDialogButtonBox::ButtonRole::HelpRole);
41         [[fallthrough]];
42     case ButtonConfig::Dual:
43         buttons->addButton(config.button_text[0].empty()
44                                ? tr(SWKBD_BUTTON_CANCEL)
45                                : QString::fromStdString(config.button_text[0]),
46                            QDialogButtonBox::ButtonRole::RejectRole);
47         [[fallthrough]];
48     case ButtonConfig::Single:
49         buttons->addButton(config.button_text[2].empty()
50                                ? tr(SWKBD_BUTTON_OKAY)
51                                : QString::fromStdString(config.button_text[2]),
52                            QDialogButtonBox::ButtonRole::AcceptRole);
53         break;
54     case ButtonConfig::None:
55         break;
56     }
57     connect(buttons, &QDialogButtonBox::accepted, this, [=] { Submit(); });
58     connect(buttons, &QDialogButtonBox::rejected, this, [=] {
59         button = QtKeyboard::cancel_id;
60         accept();
61     });
62     connect(buttons, &QDialogButtonBox::helpRequested, this, [=] {
63         button = QtKeyboard::forgot_id;
64         accept();
65     });
66     layout->addWidget(label);
67     layout->addWidget(line_edit);
68     layout->addWidget(buttons);
69     setLayout(layout);
70 }
71 
Submit()72 void QtKeyboardDialog::Submit() {
73     auto error = keyboard->ValidateInput(line_edit->text().toStdString());
74     if (error != Frontend::ValidationError::None) {
75         HandleValidationError(error);
76     } else {
77         button = keyboard->ok_id;
78         text = line_edit->text();
79         accept();
80     }
81 }
82 
HandleValidationError(Frontend::ValidationError error)83 void QtKeyboardDialog::HandleValidationError(Frontend::ValidationError error) {
84     using namespace Frontend;
85     const std::unordered_map<ValidationError, QString> VALIDATION_ERROR_MESSAGES = {
86         {ValidationError::FixedLengthRequired,
87          tr("Text length is not correct (should be %1 characters)")
88              .arg(keyboard->config.max_text_length)},
89         {ValidationError::MaxLengthExceeded,
90          tr("Text is too long (should be no more than %1 characters)")
91              .arg(keyboard->config.max_text_length)},
92         {ValidationError::BlankInputNotAllowed, tr("Blank input is not allowed")},
93         {ValidationError::EmptyInputNotAllowed, tr("Empty input is not allowed")},
94     };
95     QMessageBox::critical(this, tr("Validation error"), VALIDATION_ERROR_MESSAGES.at(error));
96 }
97 
QtKeyboard(QWidget & parent_)98 QtKeyboard::QtKeyboard(QWidget& parent_) : parent(parent_) {}
99 
Execute(const Frontend::KeyboardConfig & config)100 void QtKeyboard::Execute(const Frontend::KeyboardConfig& config) {
101     SoftwareKeyboard::Execute(config);
102     if (this->config.button_config != Frontend::ButtonConfig::None) {
103         ok_id = static_cast<u8>(this->config.button_config);
104     }
105     QMetaObject::invokeMethod(this, "OpenInputDialog", Qt::BlockingQueuedConnection);
106     Finalize(result_text, result_button);
107 }
108 
ShowError(const std::string & error)109 void QtKeyboard::ShowError(const std::string& error) {
110     QString message = QString::fromStdString(error);
111     QMetaObject::invokeMethod(this, "ShowErrorDialog", Qt::BlockingQueuedConnection,
112                               Q_ARG(QString, message));
113 }
114 
OpenInputDialog()115 void QtKeyboard::OpenInputDialog() {
116     QtKeyboardDialog dialog(&parent, this);
117     dialog.setWindowFlags(dialog.windowFlags() &
118                           ~(Qt::WindowCloseButtonHint | Qt::WindowContextHelpButtonHint));
119     dialog.setWindowModality(Qt::WindowModal);
120     dialog.exec();
121 
122     result_text = dialog.text.toStdString();
123     result_button = dialog.button;
124     LOG_INFO(Frontend, "SWKBD input dialog finished, text={}, button={}", result_text,
125              result_button);
126 }
127 
ShowErrorDialog(QString message)128 void QtKeyboard::ShowErrorDialog(QString message) {
129     QMessageBox::critical(&parent, tr("Software Keyboard"), message);
130 }
131