5396593b55
Previously the text string for the inline software keyboard was being sent instead of the normal software keyboard, leading to empty text being sent all the time.
1635 lines
57 KiB
C++
1635 lines
57 KiB
C++
// Copyright 2021 yuzu Emulator Project
|
|
// Licensed under GPLv2 or any later version
|
|
// Refer to the license.txt file included.
|
|
|
|
#include <QCursor>
|
|
#include <QKeyEvent>
|
|
#include <QScreen>
|
|
|
|
#include "common/logging/log.h"
|
|
#include "common/settings.h"
|
|
#include "common/string_util.h"
|
|
#include "core/core.h"
|
|
#include "core/frontend/input_interpreter.h"
|
|
#include "ui_software_keyboard.h"
|
|
#include "yuzu/applets/software_keyboard.h"
|
|
#include "yuzu/main.h"
|
|
#include "yuzu/util/overlay_dialog.h"
|
|
|
|
namespace {
|
|
|
|
using namespace Service::AM::Applets;
|
|
|
|
constexpr float BASE_HEADER_FONT_SIZE = 23.0f;
|
|
constexpr float BASE_SUB_FONT_SIZE = 17.0f;
|
|
constexpr float BASE_EDITOR_FONT_SIZE = 26.0f;
|
|
constexpr float BASE_CHAR_BUTTON_FONT_SIZE = 28.0f;
|
|
constexpr float BASE_LABEL_BUTTON_FONT_SIZE = 18.0f;
|
|
constexpr float BASE_ICON_BUTTON_SIZE = 36.0f;
|
|
[[maybe_unused]] constexpr float BASE_WIDTH = 1280.0f;
|
|
constexpr float BASE_HEIGHT = 720.0f;
|
|
|
|
} // Anonymous namespace
|
|
|
|
QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
|
|
QWidget* parent, Core::System& system_, bool is_inline_,
|
|
Core::Frontend::KeyboardInitializeParameters initialize_parameters_)
|
|
: QDialog(parent), ui{std::make_unique<Ui::QtSoftwareKeyboardDialog>()}, system{system_},
|
|
is_inline{is_inline_}, initialize_parameters{std::move(initialize_parameters_)} {
|
|
ui->setupUi(this);
|
|
|
|
setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint |
|
|
Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint);
|
|
setWindowModality(Qt::WindowModal);
|
|
setAttribute(Qt::WA_DeleteOnClose);
|
|
setAttribute(Qt::WA_TranslucentBackground);
|
|
|
|
keyboard_buttons = {{
|
|
{{
|
|
{
|
|
ui->button_1,
|
|
ui->button_2,
|
|
ui->button_3,
|
|
ui->button_4,
|
|
ui->button_5,
|
|
ui->button_6,
|
|
ui->button_7,
|
|
ui->button_8,
|
|
ui->button_9,
|
|
ui->button_0,
|
|
ui->button_minus,
|
|
ui->button_backspace,
|
|
},
|
|
{
|
|
ui->button_q,
|
|
ui->button_w,
|
|
ui->button_e,
|
|
ui->button_r,
|
|
ui->button_t,
|
|
ui->button_y,
|
|
ui->button_u,
|
|
ui->button_i,
|
|
ui->button_o,
|
|
ui->button_p,
|
|
ui->button_slash,
|
|
ui->button_return,
|
|
},
|
|
{
|
|
ui->button_a,
|
|
ui->button_s,
|
|
ui->button_d,
|
|
ui->button_f,
|
|
ui->button_g,
|
|
ui->button_h,
|
|
ui->button_j,
|
|
ui->button_k,
|
|
ui->button_l,
|
|
ui->button_colon,
|
|
ui->button_apostrophe,
|
|
ui->button_return,
|
|
},
|
|
{
|
|
ui->button_z,
|
|
ui->button_x,
|
|
ui->button_c,
|
|
ui->button_v,
|
|
ui->button_b,
|
|
ui->button_n,
|
|
ui->button_m,
|
|
ui->button_comma,
|
|
ui->button_dot,
|
|
ui->button_question,
|
|
ui->button_exclamation,
|
|
ui->button_ok,
|
|
},
|
|
{
|
|
ui->button_shift,
|
|
ui->button_shift,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_space,
|
|
ui->button_ok,
|
|
},
|
|
}},
|
|
{{
|
|
{
|
|
ui->button_hash,
|
|
ui->button_left_bracket,
|
|
ui->button_right_bracket,
|
|
ui->button_dollar,
|
|
ui->button_percent,
|
|
ui->button_circumflex,
|
|
ui->button_ampersand,
|
|
ui->button_asterisk,
|
|
ui->button_left_parenthesis,
|
|
ui->button_right_parenthesis,
|
|
ui->button_underscore,
|
|
ui->button_backspace_shift,
|
|
},
|
|
{
|
|
ui->button_q_shift,
|
|
ui->button_w_shift,
|
|
ui->button_e_shift,
|
|
ui->button_r_shift,
|
|
ui->button_t_shift,
|
|
ui->button_y_shift,
|
|
ui->button_u_shift,
|
|
ui->button_i_shift,
|
|
ui->button_o_shift,
|
|
ui->button_p_shift,
|
|
ui->button_at,
|
|
ui->button_return_shift,
|
|
},
|
|
{
|
|
ui->button_a_shift,
|
|
ui->button_s_shift,
|
|
ui->button_d_shift,
|
|
ui->button_f_shift,
|
|
ui->button_g_shift,
|
|
ui->button_h_shift,
|
|
ui->button_j_shift,
|
|
ui->button_k_shift,
|
|
ui->button_l_shift,
|
|
ui->button_semicolon,
|
|
ui->button_quotation,
|
|
ui->button_return_shift,
|
|
},
|
|
{
|
|
ui->button_z_shift,
|
|
ui->button_x_shift,
|
|
ui->button_c_shift,
|
|
ui->button_v_shift,
|
|
ui->button_b_shift,
|
|
ui->button_n_shift,
|
|
ui->button_m_shift,
|
|
ui->button_less_than,
|
|
ui->button_greater_than,
|
|
ui->button_plus,
|
|
ui->button_equal,
|
|
ui->button_ok_shift,
|
|
},
|
|
{
|
|
ui->button_shift_shift,
|
|
ui->button_shift_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_space_shift,
|
|
ui->button_ok_shift,
|
|
},
|
|
}},
|
|
}};
|
|
|
|
numberpad_buttons = {{
|
|
{
|
|
ui->button_1_num,
|
|
ui->button_2_num,
|
|
ui->button_3_num,
|
|
ui->button_backspace_num,
|
|
},
|
|
{
|
|
ui->button_4_num,
|
|
ui->button_5_num,
|
|
ui->button_6_num,
|
|
ui->button_ok_num,
|
|
},
|
|
{
|
|
ui->button_7_num,
|
|
ui->button_8_num,
|
|
ui->button_9_num,
|
|
ui->button_ok_num,
|
|
},
|
|
{
|
|
nullptr,
|
|
ui->button_0_num,
|
|
nullptr,
|
|
ui->button_ok_num,
|
|
},
|
|
}};
|
|
|
|
all_buttons = {
|
|
ui->button_1,
|
|
ui->button_2,
|
|
ui->button_3,
|
|
ui->button_4,
|
|
ui->button_5,
|
|
ui->button_6,
|
|
ui->button_7,
|
|
ui->button_8,
|
|
ui->button_9,
|
|
ui->button_0,
|
|
ui->button_minus,
|
|
ui->button_backspace,
|
|
ui->button_q,
|
|
ui->button_w,
|
|
ui->button_e,
|
|
ui->button_r,
|
|
ui->button_t,
|
|
ui->button_y,
|
|
ui->button_u,
|
|
ui->button_i,
|
|
ui->button_o,
|
|
ui->button_p,
|
|
ui->button_slash,
|
|
ui->button_return,
|
|
ui->button_a,
|
|
ui->button_s,
|
|
ui->button_d,
|
|
ui->button_f,
|
|
ui->button_g,
|
|
ui->button_h,
|
|
ui->button_j,
|
|
ui->button_k,
|
|
ui->button_l,
|
|
ui->button_colon,
|
|
ui->button_apostrophe,
|
|
ui->button_z,
|
|
ui->button_x,
|
|
ui->button_c,
|
|
ui->button_v,
|
|
ui->button_b,
|
|
ui->button_n,
|
|
ui->button_m,
|
|
ui->button_comma,
|
|
ui->button_dot,
|
|
ui->button_question,
|
|
ui->button_exclamation,
|
|
ui->button_ok,
|
|
ui->button_shift,
|
|
ui->button_space,
|
|
ui->button_hash,
|
|
ui->button_left_bracket,
|
|
ui->button_right_bracket,
|
|
ui->button_dollar,
|
|
ui->button_percent,
|
|
ui->button_circumflex,
|
|
ui->button_ampersand,
|
|
ui->button_asterisk,
|
|
ui->button_left_parenthesis,
|
|
ui->button_right_parenthesis,
|
|
ui->button_underscore,
|
|
ui->button_backspace_shift,
|
|
ui->button_q_shift,
|
|
ui->button_w_shift,
|
|
ui->button_e_shift,
|
|
ui->button_r_shift,
|
|
ui->button_t_shift,
|
|
ui->button_y_shift,
|
|
ui->button_u_shift,
|
|
ui->button_i_shift,
|
|
ui->button_o_shift,
|
|
ui->button_p_shift,
|
|
ui->button_at,
|
|
ui->button_return_shift,
|
|
ui->button_a_shift,
|
|
ui->button_s_shift,
|
|
ui->button_d_shift,
|
|
ui->button_f_shift,
|
|
ui->button_g_shift,
|
|
ui->button_h_shift,
|
|
ui->button_j_shift,
|
|
ui->button_k_shift,
|
|
ui->button_l_shift,
|
|
ui->button_semicolon,
|
|
ui->button_quotation,
|
|
ui->button_z_shift,
|
|
ui->button_x_shift,
|
|
ui->button_c_shift,
|
|
ui->button_v_shift,
|
|
ui->button_b_shift,
|
|
ui->button_n_shift,
|
|
ui->button_m_shift,
|
|
ui->button_less_than,
|
|
ui->button_greater_than,
|
|
ui->button_plus,
|
|
ui->button_equal,
|
|
ui->button_ok_shift,
|
|
ui->button_shift_shift,
|
|
ui->button_space_shift,
|
|
ui->button_1_num,
|
|
ui->button_2_num,
|
|
ui->button_3_num,
|
|
ui->button_backspace_num,
|
|
ui->button_4_num,
|
|
ui->button_5_num,
|
|
ui->button_6_num,
|
|
ui->button_ok_num,
|
|
ui->button_7_num,
|
|
ui->button_8_num,
|
|
ui->button_9_num,
|
|
ui->button_0_num,
|
|
};
|
|
|
|
SetupMouseHover();
|
|
|
|
if (!initialize_parameters.ok_text.empty()) {
|
|
ui->button_ok->setText(QString::fromStdU16String(initialize_parameters.ok_text));
|
|
}
|
|
|
|
ui->label_header->setText(QString::fromStdU16String(initialize_parameters.header_text));
|
|
ui->label_sub->setText(QString::fromStdU16String(initialize_parameters.sub_text));
|
|
|
|
current_text = initialize_parameters.initial_text;
|
|
cursor_position = initialize_parameters.initial_cursor_position;
|
|
|
|
SetTextDrawType();
|
|
|
|
for (auto* button : all_buttons) {
|
|
connect(button, &QPushButton::clicked, this, [this, button](bool) {
|
|
if (is_inline) {
|
|
InlineKeyboardButtonClicked(button);
|
|
} else {
|
|
NormalKeyboardButtonClicked(button);
|
|
}
|
|
});
|
|
}
|
|
|
|
// TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
|
|
if (system.IsPoweredOn()) {
|
|
input_interpreter = std::make_unique<InputInterpreter>(system);
|
|
}
|
|
}
|
|
|
|
QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() {
|
|
StopInputThread();
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) {
|
|
if (isVisible()) {
|
|
return;
|
|
}
|
|
|
|
MoveAndResizeWindow(pos, size);
|
|
|
|
SetKeyboardType();
|
|
SetPasswordMode();
|
|
SetControllerImage();
|
|
DisableKeyboardButtons();
|
|
SetBackspaceOkEnabled();
|
|
|
|
open();
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::ShowTextCheckDialog(
|
|
Service::AM::Applets::SwkbdTextCheckResult text_check_result,
|
|
std::u16string text_check_message) {
|
|
switch (text_check_result) {
|
|
case SwkbdTextCheckResult::Success:
|
|
case SwkbdTextCheckResult::Silent:
|
|
default:
|
|
break;
|
|
case SwkbdTextCheckResult::Failure: {
|
|
StopInputThread();
|
|
|
|
OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message),
|
|
QString{}, tr("OK"), Qt::AlignCenter);
|
|
dialog.exec();
|
|
|
|
StartInputThread();
|
|
break;
|
|
}
|
|
case SwkbdTextCheckResult::Confirm: {
|
|
StopInputThread();
|
|
|
|
OverlayDialog dialog(this, system, QString{}, QString::fromStdU16String(text_check_message),
|
|
tr("Cancel"), tr("OK"), Qt::AlignCenter);
|
|
if (dialog.exec() != QDialog::Accepted) {
|
|
StartInputThread();
|
|
break;
|
|
}
|
|
|
|
auto text = ui->topOSK->currentIndex() == 1
|
|
? ui->text_edit_osk->toPlainText().toStdU16String()
|
|
: ui->line_edit_osk->text().toStdU16String();
|
|
|
|
emit SubmitNormalText(SwkbdResult::Ok, std::move(text));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::ShowInlineKeyboard(
|
|
Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos, QSize size) {
|
|
MoveAndResizeWindow(pos, size);
|
|
|
|
ui->topOSK->setStyleSheet(QStringLiteral("background: rgba(0, 0, 0, 0);"));
|
|
|
|
ui->headerOSK->hide();
|
|
ui->subOSK->hide();
|
|
ui->inputOSK->hide();
|
|
ui->charactersOSK->hide();
|
|
ui->inputBoxOSK->hide();
|
|
ui->charactersBoxOSK->hide();
|
|
|
|
initialize_parameters.max_text_length = appear_parameters.max_text_length;
|
|
initialize_parameters.min_text_length = appear_parameters.min_text_length;
|
|
initialize_parameters.type = appear_parameters.type;
|
|
initialize_parameters.key_disable_flags = appear_parameters.key_disable_flags;
|
|
initialize_parameters.enable_backspace_button = appear_parameters.enable_backspace_button;
|
|
initialize_parameters.enable_return_button = appear_parameters.enable_return_button;
|
|
initialize_parameters.disable_cancel_button = initialize_parameters.disable_cancel_button;
|
|
|
|
SetKeyboardType();
|
|
SetControllerImage();
|
|
DisableKeyboardButtons();
|
|
SetBackspaceOkEnabled();
|
|
|
|
open();
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::HideInlineKeyboard() {
|
|
StopInputThread();
|
|
QDialog::hide();
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::InlineTextChanged(
|
|
Core::Frontend::InlineTextParameters text_parameters) {
|
|
current_text = text_parameters.input_text;
|
|
cursor_position = text_parameters.cursor_position;
|
|
|
|
SetBackspaceOkEnabled();
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::ExitKeyboard() {
|
|
StopInputThread();
|
|
QDialog::done(QDialog::Accepted);
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::open() {
|
|
QDialog::open();
|
|
|
|
row = 0;
|
|
column = 0;
|
|
|
|
const auto* const curr_button =
|
|
keyboard_buttons[static_cast<int>(bottom_osk_index)][row][column];
|
|
|
|
// This is a workaround for setFocus() randomly not showing focus in the UI
|
|
QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
|
|
|
|
StartInputThread();
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::reject() {
|
|
// Pressing the ESC key in a dialog calls QDialog::reject().
|
|
// We will override this behavior to the "Cancel" action on the software keyboard.
|
|
TranslateButtonPress(HIDButton::X);
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
|
|
if (!is_inline) {
|
|
QDialog::keyPressEvent(event);
|
|
return;
|
|
}
|
|
|
|
const auto entered_key = event->key();
|
|
|
|
switch (entered_key) {
|
|
case Qt::Key_Escape:
|
|
QDialog::keyPressEvent(event);
|
|
return;
|
|
case Qt::Key_Backspace:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_backspace->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_backspace_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
ui->button_backspace_num->click();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
case Qt::Key_Return:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_ok->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_ok_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
ui->button_ok_num->click();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return;
|
|
case Qt::Key_Left:
|
|
MoveTextCursorDirection(Direction::Left);
|
|
return;
|
|
case Qt::Key_Right:
|
|
MoveTextCursorDirection(Direction::Right);
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
const auto entered_text = event->text();
|
|
|
|
if (entered_text.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
InlineTextInsertString(entered_text.toStdU16String());
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::MoveAndResizeWindow(QPoint pos, QSize size) {
|
|
QDialog::move(pos);
|
|
QDialog::resize(size);
|
|
|
|
// High DPI
|
|
const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
|
|
|
|
RescaleKeyboardElements(size.width(), size.height(), dpi_scale);
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::RescaleKeyboardElements(float width, float height, float dpi_scale) {
|
|
const auto header_font_size = BASE_HEADER_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
|
|
const auto sub_font_size = BASE_SUB_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
|
|
const auto editor_font_size = BASE_EDITOR_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
|
|
const auto char_button_font_size =
|
|
BASE_CHAR_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
|
|
const auto label_button_font_size =
|
|
BASE_LABEL_BUTTON_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
|
|
|
|
QFont header_font(QStringLiteral("MS Shell Dlg 2"), header_font_size, QFont::Normal);
|
|
QFont sub_font(QStringLiteral("MS Shell Dlg 2"), sub_font_size, QFont::Normal);
|
|
QFont editor_font(QStringLiteral("MS Shell Dlg 2"), editor_font_size, QFont::Normal);
|
|
QFont char_button_font(QStringLiteral("MS Shell Dlg 2"), char_button_font_size, QFont::Normal);
|
|
QFont label_button_font(QStringLiteral("MS Shell Dlg 2"), label_button_font_size,
|
|
QFont::Normal);
|
|
|
|
ui->label_header->setFont(header_font);
|
|
ui->label_sub->setFont(sub_font);
|
|
ui->line_edit_osk->setFont(editor_font);
|
|
ui->text_edit_osk->setFont(editor_font);
|
|
ui->label_characters->setFont(sub_font);
|
|
ui->label_characters_box->setFont(sub_font);
|
|
|
|
ui->label_shift->setFont(label_button_font);
|
|
ui->label_shift_shift->setFont(label_button_font);
|
|
ui->label_cancel->setFont(label_button_font);
|
|
ui->label_cancel_shift->setFont(label_button_font);
|
|
ui->label_cancel_num->setFont(label_button_font);
|
|
ui->label_enter->setFont(label_button_font);
|
|
ui->label_enter_shift->setFont(label_button_font);
|
|
ui->label_enter_num->setFont(label_button_font);
|
|
|
|
for (auto* button : all_buttons) {
|
|
if (button == ui->button_return || button == ui->button_return_shift) {
|
|
button->setFont(label_button_font);
|
|
continue;
|
|
}
|
|
|
|
if (button == ui->button_space || button == ui->button_space_shift) {
|
|
button->setFont(label_button_font);
|
|
continue;
|
|
}
|
|
|
|
if (button == ui->button_shift || button == ui->button_shift_shift) {
|
|
button->setFont(label_button_font);
|
|
button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) *
|
|
(height / BASE_HEIGHT));
|
|
continue;
|
|
}
|
|
|
|
if (button == ui->button_backspace || button == ui->button_backspace_shift ||
|
|
button == ui->button_backspace_num) {
|
|
button->setFont(label_button_font);
|
|
button->setIconSize(QSize(BASE_ICON_BUTTON_SIZE, BASE_ICON_BUTTON_SIZE) *
|
|
(height / BASE_HEIGHT));
|
|
continue;
|
|
}
|
|
|
|
if (button == ui->button_ok || button == ui->button_ok_shift ||
|
|
button == ui->button_ok_num) {
|
|
button->setFont(label_button_font);
|
|
continue;
|
|
}
|
|
|
|
button->setFont(char_button_font);
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::SetKeyboardType() {
|
|
switch (initialize_parameters.type) {
|
|
case SwkbdType::Normal:
|
|
case SwkbdType::Qwerty:
|
|
case SwkbdType::Unknown3:
|
|
case SwkbdType::Latin:
|
|
case SwkbdType::SimplifiedChinese:
|
|
case SwkbdType::TraditionalChinese:
|
|
case SwkbdType::Korean:
|
|
default: {
|
|
bottom_osk_index = BottomOSKIndex::LowerCase;
|
|
ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
|
|
|
|
ui->verticalLayout_2->setStretch(0, 320);
|
|
ui->verticalLayout_2->setStretch(1, 400);
|
|
|
|
ui->gridLineOSK->setRowStretch(5, 94);
|
|
ui->gridBoxOSK->setRowStretch(2, 81);
|
|
break;
|
|
}
|
|
case SwkbdType::NumberPad: {
|
|
bottom_osk_index = BottomOSKIndex::NumberPad;
|
|
ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
|
|
|
|
ui->verticalLayout_2->setStretch(0, 370);
|
|
ui->verticalLayout_2->setStretch(1, 350);
|
|
|
|
ui->gridLineOSK->setRowStretch(5, 144);
|
|
ui->gridBoxOSK->setRowStretch(2, 131);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::SetPasswordMode() {
|
|
switch (initialize_parameters.password_mode) {
|
|
case SwkbdPasswordMode::Disabled:
|
|
default:
|
|
ui->line_edit_osk->setEchoMode(QLineEdit::Normal);
|
|
break;
|
|
case SwkbdPasswordMode::Enabled:
|
|
ui->line_edit_osk->setEchoMode(QLineEdit::Password);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::SetTextDrawType() {
|
|
switch (initialize_parameters.text_draw_type) {
|
|
case SwkbdTextDrawType::Line:
|
|
case SwkbdTextDrawType::DownloadCode: {
|
|
ui->topOSK->setCurrentIndex(0);
|
|
|
|
if (initialize_parameters.max_text_length <= 10) {
|
|
ui->gridLineOSK->setColumnStretch(0, 390);
|
|
ui->gridLineOSK->setColumnStretch(1, 500);
|
|
ui->gridLineOSK->setColumnStretch(2, 390);
|
|
} else {
|
|
ui->gridLineOSK->setColumnStretch(0, 130);
|
|
ui->gridLineOSK->setColumnStretch(1, 1020);
|
|
ui->gridLineOSK->setColumnStretch(2, 130);
|
|
}
|
|
|
|
if (is_inline) {
|
|
return;
|
|
}
|
|
|
|
connect(ui->line_edit_osk, &QLineEdit::textChanged, [this](const QString& changed_string) {
|
|
const auto is_valid = ValidateInputText(changed_string);
|
|
|
|
const auto text_length = static_cast<u32>(changed_string.length());
|
|
|
|
ui->label_characters->setText(QStringLiteral("%1/%2")
|
|
.arg(text_length)
|
|
.arg(initialize_parameters.max_text_length));
|
|
|
|
ui->button_ok->setEnabled(is_valid);
|
|
ui->button_ok_shift->setEnabled(is_valid);
|
|
ui->button_ok_num->setEnabled(is_valid);
|
|
|
|
ui->line_edit_osk->setFocus();
|
|
});
|
|
|
|
connect(ui->line_edit_osk, &QLineEdit::cursorPositionChanged,
|
|
[this](int old_cursor_position, int new_cursor_position) {
|
|
ui->button_backspace->setEnabled(
|
|
initialize_parameters.enable_backspace_button && new_cursor_position > 0);
|
|
ui->button_backspace_shift->setEnabled(
|
|
initialize_parameters.enable_backspace_button && new_cursor_position > 0);
|
|
ui->button_backspace_num->setEnabled(
|
|
initialize_parameters.enable_backspace_button && new_cursor_position > 0);
|
|
|
|
ui->line_edit_osk->setFocus();
|
|
});
|
|
|
|
connect(ui->line_edit_osk, &QLineEdit::returnPressed, [this] {
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_ok->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_ok_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
ui->button_ok_num->click();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
});
|
|
|
|
ui->line_edit_osk->setPlaceholderText(
|
|
QString::fromStdU16String(initialize_parameters.guide_text));
|
|
ui->line_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text));
|
|
ui->line_edit_osk->setMaxLength(initialize_parameters.max_text_length);
|
|
ui->line_edit_osk->setCursorPosition(initialize_parameters.initial_cursor_position);
|
|
|
|
ui->label_characters->setText(QStringLiteral("%1/%2")
|
|
.arg(initialize_parameters.initial_text.size())
|
|
.arg(initialize_parameters.max_text_length));
|
|
break;
|
|
}
|
|
case SwkbdTextDrawType::Box:
|
|
default: {
|
|
ui->topOSK->setCurrentIndex(1);
|
|
|
|
if (is_inline) {
|
|
return;
|
|
}
|
|
|
|
connect(ui->text_edit_osk, &QTextEdit::textChanged, [this] {
|
|
if (static_cast<u32>(ui->text_edit_osk->toPlainText().length()) >
|
|
initialize_parameters.max_text_length) {
|
|
auto text_cursor = ui->text_edit_osk->textCursor();
|
|
ui->text_edit_osk->setTextCursor(text_cursor);
|
|
text_cursor.deletePreviousChar();
|
|
}
|
|
|
|
const auto is_valid = ValidateInputText(ui->text_edit_osk->toPlainText());
|
|
|
|
const auto text_length = static_cast<u32>(ui->text_edit_osk->toPlainText().length());
|
|
|
|
ui->label_characters_box->setText(QStringLiteral("%1/%2")
|
|
.arg(text_length)
|
|
.arg(initialize_parameters.max_text_length));
|
|
|
|
ui->button_ok->setEnabled(is_valid);
|
|
ui->button_ok_shift->setEnabled(is_valid);
|
|
ui->button_ok_num->setEnabled(is_valid);
|
|
|
|
ui->text_edit_osk->setFocus();
|
|
});
|
|
|
|
connect(ui->text_edit_osk, &QTextEdit::cursorPositionChanged, [this] {
|
|
const auto new_cursor_position = ui->text_edit_osk->textCursor().position();
|
|
|
|
ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
new_cursor_position > 0);
|
|
ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
new_cursor_position > 0);
|
|
ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
new_cursor_position > 0);
|
|
|
|
ui->text_edit_osk->setFocus();
|
|
});
|
|
|
|
ui->text_edit_osk->setPlaceholderText(
|
|
QString::fromStdU16String(initialize_parameters.guide_text));
|
|
ui->text_edit_osk->setText(QString::fromStdU16String(initialize_parameters.initial_text));
|
|
ui->text_edit_osk->moveCursor(initialize_parameters.initial_cursor_position == 0
|
|
? QTextCursor::Start
|
|
: QTextCursor::End);
|
|
|
|
ui->label_characters_box->setText(QStringLiteral("%1/%2")
|
|
.arg(initialize_parameters.initial_text.size())
|
|
.arg(initialize_parameters.max_text_length));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::SetControllerImage() {
|
|
const auto controller_type = Settings::values.players.GetValue()[8].connected
|
|
? Settings::values.players.GetValue()[8].controller_type
|
|
: Settings::values.players.GetValue()[0].controller_type;
|
|
|
|
const QString theme = [] {
|
|
if (QIcon::themeName().contains(QStringLiteral("dark")) ||
|
|
QIcon::themeName().contains(QStringLiteral("midnight"))) {
|
|
return QStringLiteral("_dark");
|
|
} else {
|
|
return QString{};
|
|
}
|
|
}();
|
|
|
|
switch (controller_type) {
|
|
case Settings::ControllerType::ProController:
|
|
case Settings::ControllerType::GameCube:
|
|
ui->icon_controller->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
|
|
ui->icon_controller_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
|
|
ui->icon_controller_num->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_pro%1.png);").arg(theme));
|
|
break;
|
|
case Settings::ControllerType::DualJoyconDetached:
|
|
ui->icon_controller->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
|
|
ui->icon_controller_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
|
|
ui->icon_controller_num->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_dual_joycon%1.png);").arg(theme));
|
|
break;
|
|
case Settings::ControllerType::LeftJoycon:
|
|
ui->icon_controller->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
|
|
.arg(theme));
|
|
ui->icon_controller_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
|
|
.arg(theme));
|
|
ui->icon_controller_num->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_single_joycon_left%1.png);")
|
|
.arg(theme));
|
|
break;
|
|
case Settings::ControllerType::RightJoycon:
|
|
ui->icon_controller->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
|
|
.arg(theme));
|
|
ui->icon_controller_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
|
|
.arg(theme));
|
|
ui->icon_controller_num->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_single_joycon_right%1.png);")
|
|
.arg(theme));
|
|
break;
|
|
case Settings::ControllerType::Handheld:
|
|
ui->icon_controller->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
|
|
ui->icon_controller_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
|
|
ui->icon_controller_num->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/controller_handheld%1.png);").arg(theme));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::DisableKeyboardButtons() {
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
case BottomOSKIndex::UpperCase:
|
|
default: {
|
|
for (const auto& keys : keyboard_buttons) {
|
|
for (const auto& rows : keys) {
|
|
for (auto* button : rows) {
|
|
if (!button) {
|
|
continue;
|
|
}
|
|
|
|
button->setEnabled(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
const auto& key_disable_flags = initialize_parameters.key_disable_flags;
|
|
|
|
ui->button_space->setDisabled(key_disable_flags.space);
|
|
ui->button_space_shift->setDisabled(key_disable_flags.space);
|
|
|
|
ui->button_at->setDisabled(key_disable_flags.at || key_disable_flags.username);
|
|
|
|
ui->button_percent->setDisabled(key_disable_flags.percent || key_disable_flags.username);
|
|
|
|
ui->button_slash->setDisabled(key_disable_flags.slash);
|
|
|
|
ui->button_1->setDisabled(key_disable_flags.numbers);
|
|
ui->button_2->setDisabled(key_disable_flags.numbers);
|
|
ui->button_3->setDisabled(key_disable_flags.numbers);
|
|
ui->button_4->setDisabled(key_disable_flags.numbers);
|
|
ui->button_5->setDisabled(key_disable_flags.numbers);
|
|
ui->button_6->setDisabled(key_disable_flags.numbers);
|
|
ui->button_7->setDisabled(key_disable_flags.numbers);
|
|
ui->button_8->setDisabled(key_disable_flags.numbers);
|
|
ui->button_9->setDisabled(key_disable_flags.numbers);
|
|
ui->button_0->setDisabled(key_disable_flags.numbers);
|
|
|
|
ui->button_return->setEnabled(initialize_parameters.enable_return_button);
|
|
ui->button_return_shift->setEnabled(initialize_parameters.enable_return_button);
|
|
break;
|
|
}
|
|
case BottomOSKIndex::NumberPad: {
|
|
for (const auto& rows : numberpad_buttons) {
|
|
for (auto* button : rows) {
|
|
if (!button) {
|
|
continue;
|
|
}
|
|
|
|
button->setEnabled(true);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::SetBackspaceOkEnabled() {
|
|
if (is_inline) {
|
|
ui->button_ok->setEnabled(current_text.size() >= initialize_parameters.min_text_length);
|
|
ui->button_ok_shift->setEnabled(current_text.size() >=
|
|
initialize_parameters.min_text_length);
|
|
ui->button_ok_num->setEnabled(current_text.size() >= initialize_parameters.min_text_length);
|
|
|
|
ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
cursor_position > 0);
|
|
ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
cursor_position > 0);
|
|
ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
cursor_position > 0);
|
|
} else {
|
|
const auto text_length = [this] {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
return static_cast<u32>(ui->text_edit_osk->toPlainText().length());
|
|
} else {
|
|
return static_cast<u32>(ui->line_edit_osk->text().length());
|
|
}
|
|
}();
|
|
|
|
const auto normal_cursor_position = [this] {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
return ui->text_edit_osk->textCursor().position();
|
|
} else {
|
|
return ui->line_edit_osk->cursorPosition();
|
|
}
|
|
}();
|
|
|
|
ui->button_ok->setEnabled(text_length >= initialize_parameters.min_text_length);
|
|
ui->button_ok_shift->setEnabled(text_length >= initialize_parameters.min_text_length);
|
|
ui->button_ok_num->setEnabled(text_length >= initialize_parameters.min_text_length);
|
|
|
|
ui->button_backspace->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
normal_cursor_position > 0);
|
|
ui->button_backspace_shift->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
normal_cursor_position > 0);
|
|
ui->button_backspace_num->setEnabled(initialize_parameters.enable_backspace_button &&
|
|
normal_cursor_position > 0);
|
|
}
|
|
}
|
|
|
|
bool QtSoftwareKeyboardDialog::ValidateInputText(const QString& input_text) {
|
|
const auto& key_disable_flags = initialize_parameters.key_disable_flags;
|
|
|
|
const auto input_text_length = static_cast<u32>(input_text.length());
|
|
|
|
if (input_text_length < initialize_parameters.min_text_length ||
|
|
input_text_length > initialize_parameters.max_text_length) {
|
|
return false;
|
|
}
|
|
|
|
if (key_disable_flags.space && input_text.contains(QLatin1Char{' '})) {
|
|
return false;
|
|
}
|
|
|
|
if ((key_disable_flags.at || key_disable_flags.username) &&
|
|
input_text.contains(QLatin1Char{'@'})) {
|
|
return false;
|
|
}
|
|
|
|
if ((key_disable_flags.percent || key_disable_flags.username) &&
|
|
input_text.contains(QLatin1Char{'%'})) {
|
|
return false;
|
|
}
|
|
|
|
if (key_disable_flags.slash && input_text.contains(QLatin1Char{'/'})) {
|
|
return false;
|
|
}
|
|
|
|
if ((key_disable_flags.backslash || key_disable_flags.username) &&
|
|
input_text.contains(QLatin1Char('\\'))) {
|
|
return false;
|
|
}
|
|
|
|
if (key_disable_flags.numbers &&
|
|
std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return c.isDigit(); })) {
|
|
return false;
|
|
}
|
|
|
|
if (bottom_osk_index == BottomOSKIndex::NumberPad &&
|
|
std::any_of(input_text.begin(), input_text.end(), [](QChar c) { return !c.isDigit(); })) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::ChangeBottomOSKIndex() {
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
bottom_osk_index = BottomOSKIndex::UpperCase;
|
|
ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
|
|
|
|
ui->button_shift_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/osk_button_shift_lock_off.png);"
|
|
"\nimage-position: left;"));
|
|
|
|
ui->button_shift_shift->setIconSize(ui->button_shift->iconSize());
|
|
ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize());
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
if (caps_lock_enabled) {
|
|
caps_lock_enabled = false;
|
|
|
|
ui->button_shift_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/osk_button_shift_lock_off.png);"
|
|
"\nimage-position: left;"));
|
|
|
|
ui->button_shift_shift->setIconSize(ui->button_shift->iconSize());
|
|
ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize());
|
|
|
|
ui->label_shift_shift->setText(QStringLiteral("Caps Lock"));
|
|
|
|
bottom_osk_index = BottomOSKIndex::LowerCase;
|
|
ui->bottomOSK->setCurrentIndex(static_cast<int>(bottom_osk_index));
|
|
} else {
|
|
caps_lock_enabled = true;
|
|
|
|
ui->button_shift_shift->setStyleSheet(
|
|
QStringLiteral("image: url(:/overlay/osk_button_shift_lock_on.png);"
|
|
"\nimage-position: left;"));
|
|
|
|
ui->button_shift_shift->setIconSize(ui->button_shift->iconSize());
|
|
ui->button_backspace_shift->setIconSize(ui->button_backspace->iconSize());
|
|
|
|
ui->label_shift_shift->setText(QStringLiteral("Caps Lock Off"));
|
|
}
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::NormalKeyboardButtonClicked(QPushButton* button) {
|
|
if (button == ui->button_ampersand) {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
ui->text_edit_osk->insertPlainText(QStringLiteral("&"));
|
|
} else {
|
|
ui->line_edit_osk->insert(QStringLiteral("&"));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_return || button == ui->button_return_shift) {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
ui->text_edit_osk->insertPlainText(QStringLiteral("\n"));
|
|
} else {
|
|
ui->line_edit_osk->insert(QStringLiteral("\n"));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_space || button == ui->button_space_shift) {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
ui->text_edit_osk->insertPlainText(QStringLiteral(" "));
|
|
} else {
|
|
ui->line_edit_osk->insert(QStringLiteral(" "));
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_shift || button == ui->button_shift_shift) {
|
|
ChangeBottomOSKIndex();
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_backspace || button == ui->button_backspace_shift ||
|
|
button == ui->button_backspace_num) {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
auto text_cursor = ui->text_edit_osk->textCursor();
|
|
ui->text_edit_osk->setTextCursor(text_cursor);
|
|
text_cursor.deletePreviousChar();
|
|
} else {
|
|
ui->line_edit_osk->backspace();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
emit SubmitNormalText(SwkbdResult::Ok,
|
|
ui->text_edit_osk->toPlainText().toStdU16String());
|
|
} else {
|
|
emit SubmitNormalText(SwkbdResult::Ok, ui->line_edit_osk->text().toStdU16String());
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
ui->text_edit_osk->insertPlainText(button->text());
|
|
} else {
|
|
ui->line_edit_osk->insert(button->text());
|
|
}
|
|
|
|
// Revert the keyboard to lowercase if the shift key is active.
|
|
if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
|
|
// This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase
|
|
// if bottom_osk_index is UpperCase and caps_lock_enabled is true.
|
|
caps_lock_enabled = true;
|
|
ChangeBottomOSKIndex();
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::InlineKeyboardButtonClicked(QPushButton* button) {
|
|
if (!button->isEnabled()) {
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_ampersand) {
|
|
InlineTextInsertString(u"&");
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_return || button == ui->button_return_shift) {
|
|
InlineTextInsertString(u"\n");
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_space || button == ui->button_space_shift) {
|
|
InlineTextInsertString(u" ");
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_shift || button == ui->button_shift_shift) {
|
|
ChangeBottomOSKIndex();
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_backspace || button == ui->button_backspace_shift ||
|
|
button == ui->button_backspace_num) {
|
|
if (cursor_position <= 0 || current_text.empty()) {
|
|
cursor_position = 0;
|
|
return;
|
|
}
|
|
|
|
--cursor_position;
|
|
|
|
current_text.erase(cursor_position, 1);
|
|
|
|
SetBackspaceOkEnabled();
|
|
|
|
emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position);
|
|
return;
|
|
}
|
|
|
|
if (button == ui->button_ok || button == ui->button_ok_shift || button == ui->button_ok_num) {
|
|
emit SubmitInlineText(SwkbdReplyType::DecidedEnter, current_text, cursor_position);
|
|
return;
|
|
}
|
|
|
|
InlineTextInsertString(button->text().toStdU16String());
|
|
|
|
// Revert the keyboard to lowercase if the shift key is active.
|
|
if (bottom_osk_index == BottomOSKIndex::UpperCase && !caps_lock_enabled) {
|
|
// This is set to true since ChangeBottomOSKIndex will change bottom_osk_index to LowerCase
|
|
// if bottom_osk_index is UpperCase and caps_lock_enabled is true.
|
|
caps_lock_enabled = true;
|
|
ChangeBottomOSKIndex();
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::InlineTextInsertString(std::u16string_view string) {
|
|
if ((current_text.size() + string.size()) > initialize_parameters.max_text_length) {
|
|
return;
|
|
}
|
|
|
|
current_text.insert(cursor_position, string);
|
|
|
|
cursor_position += static_cast<s32>(string.size());
|
|
|
|
SetBackspaceOkEnabled();
|
|
|
|
emit SubmitInlineText(SwkbdReplyType::ChangedString, current_text, cursor_position);
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::SetupMouseHover() {
|
|
// setFocus() has a bug where continuously changing focus will cause the focus UI to
|
|
// mysteriously disappear. A workaround we have found is using the mouse to hover over
|
|
// the buttons to act in place of the button focus. As a result, we will have to set
|
|
// a blank cursor when hovering over all the buttons and set a no focus policy so the
|
|
// buttons do not stay in focus in addition to the mouse hover.
|
|
for (auto* button : all_buttons) {
|
|
button->setCursor(QCursor(Qt::BlankCursor));
|
|
button->setFocusPolicy(Qt::NoFocus);
|
|
}
|
|
}
|
|
|
|
template <HIDButton... T>
|
|
void QtSoftwareKeyboardDialog::HandleButtonPressedOnce() {
|
|
const auto f = [this](HIDButton button) {
|
|
if (input_interpreter->IsButtonPressedOnce(button)) {
|
|
TranslateButtonPress(button);
|
|
}
|
|
};
|
|
|
|
(f(T), ...);
|
|
}
|
|
|
|
template <HIDButton... T>
|
|
void QtSoftwareKeyboardDialog::HandleButtonHold() {
|
|
const auto f = [this](HIDButton button) {
|
|
if (input_interpreter->IsButtonHeld(button)) {
|
|
TranslateButtonPress(button);
|
|
}
|
|
};
|
|
|
|
(f(T), ...);
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::TranslateButtonPress(HIDButton button) {
|
|
switch (button) {
|
|
case HIDButton::A:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
case BottomOSKIndex::UpperCase:
|
|
keyboard_buttons[static_cast<std::size_t>(bottom_osk_index)][row][column]->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
numberpad_buttons[row][column]->click();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case HIDButton::B:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_backspace->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_backspace_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
ui->button_backspace_num->click();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case HIDButton::X:
|
|
if (is_inline) {
|
|
emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
|
|
} else {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
emit SubmitNormalText(SwkbdResult::Cancel,
|
|
ui->text_edit_osk->toPlainText().toStdU16String());
|
|
} else {
|
|
emit SubmitNormalText(SwkbdResult::Cancel,
|
|
ui->line_edit_osk->text().toStdU16String());
|
|
}
|
|
}
|
|
break;
|
|
case HIDButton::Y:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_space->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_space_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case HIDButton::LStick:
|
|
case HIDButton::RStick:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_shift->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_shift_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case HIDButton::L:
|
|
MoveTextCursorDirection(Direction::Left);
|
|
break;
|
|
case HIDButton::R:
|
|
MoveTextCursorDirection(Direction::Right);
|
|
break;
|
|
case HIDButton::Plus:
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
ui->button_ok->click();
|
|
break;
|
|
case BottomOSKIndex::UpperCase:
|
|
ui->button_ok_shift->click();
|
|
break;
|
|
case BottomOSKIndex::NumberPad:
|
|
ui->button_ok_num->click();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case HIDButton::DLeft:
|
|
case HIDButton::LStickLeft:
|
|
case HIDButton::RStickLeft:
|
|
MoveButtonDirection(Direction::Left);
|
|
break;
|
|
case HIDButton::DUp:
|
|
case HIDButton::LStickUp:
|
|
case HIDButton::RStickUp:
|
|
MoveButtonDirection(Direction::Up);
|
|
break;
|
|
case HIDButton::DRight:
|
|
case HIDButton::LStickRight:
|
|
case HIDButton::RStickRight:
|
|
MoveButtonDirection(Direction::Right);
|
|
break;
|
|
case HIDButton::DDown:
|
|
case HIDButton::LStickDown:
|
|
case HIDButton::RStickDown:
|
|
MoveButtonDirection(Direction::Down);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::MoveButtonDirection(Direction direction) {
|
|
// Changes the row or column index depending on the direction.
|
|
auto move_direction = [this, direction](std::size_t max_rows, std::size_t max_columns) {
|
|
switch (direction) {
|
|
case Direction::Left:
|
|
column = (column + max_columns - 1) % max_columns;
|
|
break;
|
|
case Direction::Up:
|
|
row = (row + max_rows - 1) % max_rows;
|
|
break;
|
|
case Direction::Right:
|
|
column = (column + 1) % max_columns;
|
|
break;
|
|
case Direction::Down:
|
|
row = (row + 1) % max_rows;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
};
|
|
|
|
switch (bottom_osk_index) {
|
|
case BottomOSKIndex::LowerCase:
|
|
case BottomOSKIndex::UpperCase: {
|
|
const auto index = static_cast<std::size_t>(bottom_osk_index);
|
|
|
|
const auto* const prev_button = keyboard_buttons[index][row][column];
|
|
move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL);
|
|
auto* curr_button = keyboard_buttons[index][row][column];
|
|
|
|
while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
|
|
move_direction(NUM_ROWS_NORMAL, NUM_COLUMNS_NORMAL);
|
|
curr_button = keyboard_buttons[index][row][column];
|
|
}
|
|
|
|
// This is a workaround for setFocus() randomly not showing focus in the UI
|
|
QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
|
|
break;
|
|
}
|
|
case BottomOSKIndex::NumberPad: {
|
|
const auto* const prev_button = numberpad_buttons[row][column];
|
|
move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD);
|
|
auto* curr_button = numberpad_buttons[row][column];
|
|
|
|
while (!curr_button || !curr_button->isEnabled() || curr_button == prev_button) {
|
|
move_direction(NUM_ROWS_NUMPAD, NUM_COLUMNS_NUMPAD);
|
|
curr_button = numberpad_buttons[row][column];
|
|
}
|
|
|
|
// This is a workaround for setFocus() randomly not showing focus in the UI
|
|
QCursor::setPos(curr_button->mapToGlobal(curr_button->rect().center()));
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::MoveTextCursorDirection(Direction direction) {
|
|
switch (direction) {
|
|
case Direction::Left:
|
|
if (is_inline) {
|
|
if (cursor_position <= 0) {
|
|
cursor_position = 0;
|
|
} else {
|
|
--cursor_position;
|
|
emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position);
|
|
}
|
|
} else {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
ui->text_edit_osk->moveCursor(QTextCursor::Left);
|
|
} else {
|
|
ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() - 1);
|
|
}
|
|
}
|
|
break;
|
|
case Direction::Right:
|
|
if (is_inline) {
|
|
if (cursor_position >= static_cast<s32>(current_text.size())) {
|
|
cursor_position = static_cast<s32>(current_text.size());
|
|
} else {
|
|
++cursor_position;
|
|
emit SubmitInlineText(SwkbdReplyType::MovedCursor, current_text, cursor_position);
|
|
}
|
|
} else {
|
|
if (ui->topOSK->currentIndex() == 1) {
|
|
ui->text_edit_osk->moveCursor(QTextCursor::Right);
|
|
} else {
|
|
ui->line_edit_osk->setCursorPosition(ui->line_edit_osk->cursorPosition() + 1);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::StartInputThread() {
|
|
if (input_thread_running) {
|
|
return;
|
|
}
|
|
|
|
input_thread_running = true;
|
|
|
|
input_thread = std::thread(&QtSoftwareKeyboardDialog::InputThread, this);
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::StopInputThread() {
|
|
input_thread_running = false;
|
|
|
|
if (input_thread.joinable()) {
|
|
input_thread.join();
|
|
}
|
|
|
|
if (input_interpreter) {
|
|
input_interpreter->ResetButtonStates();
|
|
}
|
|
}
|
|
|
|
void QtSoftwareKeyboardDialog::InputThread() {
|
|
while (input_thread_running) {
|
|
input_interpreter->PollInput();
|
|
|
|
HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::X, HIDButton::Y,
|
|
HIDButton::LStick, HIDButton::RStick, HIDButton::L, HIDButton::R,
|
|
HIDButton::Plus, HIDButton::DLeft, HIDButton::DUp,
|
|
HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft,
|
|
HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown,
|
|
HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight,
|
|
HIDButton::RStickDown>();
|
|
|
|
HandleButtonHold<HIDButton::B, HIDButton::L, HIDButton::R, HIDButton::DLeft, HIDButton::DUp,
|
|
HIDButton::DRight, HIDButton::DDown, HIDButton::LStickLeft,
|
|
HIDButton::LStickUp, HIDButton::LStickRight, HIDButton::LStickDown,
|
|
HIDButton::RStickLeft, HIDButton::RStickUp, HIDButton::RStickRight,
|
|
HIDButton::RStickDown>();
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
|
}
|
|
}
|
|
|
|
QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
|
|
connect(this, &QtSoftwareKeyboard::MainWindowInitializeKeyboard, &main_window,
|
|
&GMainWindow::SoftwareKeyboardInitialize, Qt::QueuedConnection);
|
|
connect(this, &QtSoftwareKeyboard::MainWindowShowNormalKeyboard, &main_window,
|
|
&GMainWindow::SoftwareKeyboardShowNormal, Qt::QueuedConnection);
|
|
connect(this, &QtSoftwareKeyboard::MainWindowShowTextCheckDialog, &main_window,
|
|
&GMainWindow::SoftwareKeyboardShowTextCheck, Qt::QueuedConnection);
|
|
connect(this, &QtSoftwareKeyboard::MainWindowShowInlineKeyboard, &main_window,
|
|
&GMainWindow::SoftwareKeyboardShowInline, Qt::QueuedConnection);
|
|
connect(this, &QtSoftwareKeyboard::MainWindowHideInlineKeyboard, &main_window,
|
|
&GMainWindow::SoftwareKeyboardHideInline, Qt::QueuedConnection);
|
|
connect(this, &QtSoftwareKeyboard::MainWindowInlineTextChanged, &main_window,
|
|
&GMainWindow::SoftwareKeyboardInlineTextChanged, Qt::QueuedConnection);
|
|
connect(this, &QtSoftwareKeyboard::MainWindowExitKeyboard, &main_window,
|
|
&GMainWindow::SoftwareKeyboardExit, Qt::QueuedConnection);
|
|
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitNormalText, this,
|
|
&QtSoftwareKeyboard::SubmitNormalText, Qt::QueuedConnection);
|
|
connect(&main_window, &GMainWindow::SoftwareKeyboardSubmitInlineText, this,
|
|
&QtSoftwareKeyboard::SubmitInlineText, Qt::QueuedConnection);
|
|
}
|
|
|
|
QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
|
|
|
|
void QtSoftwareKeyboard::InitializeKeyboard(
|
|
bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters,
|
|
std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)> submit_normal_callback_,
|
|
std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
|
|
submit_inline_callback_) {
|
|
if (is_inline) {
|
|
submit_inline_callback = std::move(submit_inline_callback_);
|
|
} else {
|
|
submit_normal_callback = std::move(submit_normal_callback_);
|
|
}
|
|
|
|
LOG_INFO(Service_AM,
|
|
"\nKeyboardInitializeParameters:"
|
|
"\nok_text={}"
|
|
"\nheader_text={}"
|
|
"\nsub_text={}"
|
|
"\nguide_text={}"
|
|
"\ninitial_text={}"
|
|
"\nmax_text_length={}"
|
|
"\nmin_text_length={}"
|
|
"\ninitial_cursor_position={}"
|
|
"\ntype={}"
|
|
"\npassword_mode={}"
|
|
"\ntext_draw_type={}"
|
|
"\nkey_disable_flags={}"
|
|
"\nuse_blur_background={}"
|
|
"\nenable_backspace_button={}"
|
|
"\nenable_return_button={}"
|
|
"\ndisable_cancel_button={}",
|
|
Common::UTF16ToUTF8(initialize_parameters.ok_text),
|
|
Common::UTF16ToUTF8(initialize_parameters.header_text),
|
|
Common::UTF16ToUTF8(initialize_parameters.sub_text),
|
|
Common::UTF16ToUTF8(initialize_parameters.guide_text),
|
|
Common::UTF16ToUTF8(initialize_parameters.initial_text),
|
|
initialize_parameters.max_text_length, initialize_parameters.min_text_length,
|
|
initialize_parameters.initial_cursor_position, initialize_parameters.type,
|
|
initialize_parameters.password_mode, initialize_parameters.text_draw_type,
|
|
initialize_parameters.key_disable_flags.raw, initialize_parameters.use_blur_background,
|
|
initialize_parameters.enable_backspace_button,
|
|
initialize_parameters.enable_return_button,
|
|
initialize_parameters.disable_cancel_button);
|
|
|
|
emit MainWindowInitializeKeyboard(is_inline, std::move(initialize_parameters));
|
|
}
|
|
|
|
void QtSoftwareKeyboard::ShowNormalKeyboard() const {
|
|
emit MainWindowShowNormalKeyboard();
|
|
}
|
|
|
|
void QtSoftwareKeyboard::ShowTextCheckDialog(
|
|
Service::AM::Applets::SwkbdTextCheckResult text_check_result,
|
|
std::u16string text_check_message) const {
|
|
emit MainWindowShowTextCheckDialog(text_check_result, text_check_message);
|
|
}
|
|
|
|
void QtSoftwareKeyboard::ShowInlineKeyboard(
|
|
Core::Frontend::InlineAppearParameters appear_parameters) const {
|
|
LOG_INFO(Service_AM,
|
|
"\nInlineAppearParameters:"
|
|
"\nmax_text_length={}"
|
|
"\nmin_text_length={}"
|
|
"\nkey_top_scale_x={}"
|
|
"\nkey_top_scale_y={}"
|
|
"\nkey_top_translate_x={}"
|
|
"\nkey_top_translate_y={}"
|
|
"\ntype={}"
|
|
"\nkey_disable_flags={}"
|
|
"\nkey_top_as_floating={}"
|
|
"\nenable_backspace_button={}"
|
|
"\nenable_return_button={}"
|
|
"\ndisable_cancel_button={}",
|
|
appear_parameters.max_text_length, appear_parameters.min_text_length,
|
|
appear_parameters.key_top_scale_x, appear_parameters.key_top_scale_y,
|
|
appear_parameters.key_top_translate_x, appear_parameters.key_top_translate_y,
|
|
appear_parameters.type, appear_parameters.key_disable_flags.raw,
|
|
appear_parameters.key_top_as_floating, appear_parameters.enable_backspace_button,
|
|
appear_parameters.enable_return_button, appear_parameters.disable_cancel_button);
|
|
|
|
emit MainWindowShowInlineKeyboard(std::move(appear_parameters));
|
|
}
|
|
|
|
void QtSoftwareKeyboard::HideInlineKeyboard() const {
|
|
emit MainWindowHideInlineKeyboard();
|
|
}
|
|
|
|
void QtSoftwareKeyboard::InlineTextChanged(
|
|
Core::Frontend::InlineTextParameters text_parameters) const {
|
|
LOG_INFO(Service_AM,
|
|
"\nInlineTextParameters:"
|
|
"\ninput_text={}"
|
|
"\ncursor_position={}",
|
|
Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
|
|
|
|
emit MainWindowInlineTextChanged(std::move(text_parameters));
|
|
}
|
|
|
|
void QtSoftwareKeyboard::ExitKeyboard() const {
|
|
emit MainWindowExitKeyboard();
|
|
}
|
|
|
|
void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result,
|
|
std::u16string submitted_text) const {
|
|
submit_normal_callback(result, submitted_text);
|
|
}
|
|
|
|
void QtSoftwareKeyboard::SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
|
|
std::u16string submitted_text,
|
|
s32 cursor_position) const {
|
|
submit_inline_callback(reply_type, submitted_text, cursor_position);
|
|
}
|