Ver Fonte

Merge pull request #6133 from Morph1984/project-eleuthia

Project Eleuthia - On-Screen Keyboard and Error Applet Overlays
bunnei há 5 anos atrás
pai
commit
d5c1f3929c
99 ficheiros alterados com 9398 adições e 490 exclusões
  1. BIN
      dist/icons/overlay/arrow_left.png
  2. BIN
      dist/icons/overlay/arrow_left_dark.png
  3. BIN
      dist/icons/overlay/arrow_right.png
  4. BIN
      dist/icons/overlay/arrow_right_dark.png
  5. BIN
      dist/icons/overlay/button_A.png
  6. BIN
      dist/icons/overlay/button_A_dark.png
  7. BIN
      dist/icons/overlay/button_B.png
  8. BIN
      dist/icons/overlay/button_B_dark.png
  9. BIN
      dist/icons/overlay/button_L.png
  10. BIN
      dist/icons/overlay/button_L_dark.png
  11. BIN
      dist/icons/overlay/button_R.png
  12. BIN
      dist/icons/overlay/button_R_dark.png
  13. BIN
      dist/icons/overlay/button_X.png
  14. BIN
      dist/icons/overlay/button_X_dark.png
  15. BIN
      dist/icons/overlay/button_Y.png
  16. BIN
      dist/icons/overlay/button_Y_dark.png
  17. BIN
      dist/icons/overlay/button_minus.png
  18. BIN
      dist/icons/overlay/button_minus_dark.png
  19. BIN
      dist/icons/overlay/button_plus.png
  20. BIN
      dist/icons/overlay/button_plus_dark.png
  21. BIN
      dist/icons/overlay/button_press_stick.png
  22. BIN
      dist/icons/overlay/button_press_stick_dark.png
  23. BIN
      dist/icons/overlay/controller_dual_joycon.png
  24. BIN
      dist/icons/overlay/controller_dual_joycon_dark.png
  25. BIN
      dist/icons/overlay/controller_handheld.png
  26. BIN
      dist/icons/overlay/controller_handheld_dark.png
  27. BIN
      dist/icons/overlay/controller_pro.png
  28. BIN
      dist/icons/overlay/controller_pro_dark.png
  29. BIN
      dist/icons/overlay/controller_single_joycon_left.png
  30. BIN
      dist/icons/overlay/controller_single_joycon_left_a.png
  31. BIN
      dist/icons/overlay/controller_single_joycon_left_a_dark.png
  32. BIN
      dist/icons/overlay/controller_single_joycon_left_b.png
  33. BIN
      dist/icons/overlay/controller_single_joycon_left_b_dark.png
  34. BIN
      dist/icons/overlay/controller_single_joycon_left_dark.png
  35. BIN
      dist/icons/overlay/controller_single_joycon_left_x.png
  36. BIN
      dist/icons/overlay/controller_single_joycon_left_x_dark.png
  37. BIN
      dist/icons/overlay/controller_single_joycon_left_y.png
  38. BIN
      dist/icons/overlay/controller_single_joycon_left_y_dark.png
  39. BIN
      dist/icons/overlay/controller_single_joycon_right.png
  40. BIN
      dist/icons/overlay/controller_single_joycon_right_dark.png
  41. BIN
      dist/icons/overlay/osk_button_B.png
  42. BIN
      dist/icons/overlay/osk_button_B_dark.png
  43. BIN
      dist/icons/overlay/osk_button_B_dark_disabled.png
  44. BIN
      dist/icons/overlay/osk_button_B_disabled.png
  45. BIN
      dist/icons/overlay/osk_button_Y.png
  46. BIN
      dist/icons/overlay/osk_button_Y_dark.png
  47. BIN
      dist/icons/overlay/osk_button_Y_dark_disabled.png
  48. BIN
      dist/icons/overlay/osk_button_Y_disabled.png
  49. BIN
      dist/icons/overlay/osk_button_backspace.png
  50. BIN
      dist/icons/overlay/osk_button_backspace_dark.png
  51. BIN
      dist/icons/overlay/osk_button_plus.png
  52. BIN
      dist/icons/overlay/osk_button_plus_dark.png
  53. BIN
      dist/icons/overlay/osk_button_plus_dark_disabled.png
  54. BIN
      dist/icons/overlay/osk_button_plus_disabled.png
  55. BIN
      dist/icons/overlay/osk_button_shift.png
  56. BIN
      dist/icons/overlay/osk_button_shift_dark.png
  57. BIN
      dist/icons/overlay/osk_button_shift_lock_off.png
  58. BIN
      dist/icons/overlay/osk_button_shift_lock_on.png
  59. BIN
      dist/icons/overlay/osk_button_shift_on.png
  60. BIN
      dist/icons/overlay/osk_button_shift_on_dark.png
  61. 64 0
      dist/icons/overlay/overlay.qrc
  62. 377 0
      dist/qt_themes/default/style.qss
  63. 396 3
      dist/qt_themes/qdarkstyle/style.qss
  64. 420 19
      dist/qt_themes/qdarkstyle_midnight_blue/style.qss
  65. 1 0
      src/core/CMakeLists.txt
  66. 134 14
      src/core/frontend/applets/software_keyboard.cpp
  67. 90 28
      src/core/frontend/applets/software_keyboard.h
  68. 14 1
      src/core/frontend/input_interpreter.cpp
  69. 3 0
      src/core/frontend/input_interpreter.h
  70. 6 2
      src/core/hle/kernel/hle_ipc.cpp
  71. 10 0
      src/core/hle/kernel/hle_ipc.h
  72. 67 10
      src/core/hle/service/am/am.cpp
  73. 1 0
      src/core/hle/service/am/am.h
  74. 9 9
      src/core/hle/service/am/applets/applets.cpp
  75. 9 1
      src/core/hle/service/am/applets/applets.h
  76. 3 2
      src/core/hle/service/am/applets/controller.cpp
  77. 3 1
      src/core/hle/service/am/applets/controller.h
  78. 3 2
      src/core/hle/service/am/applets/error.cpp
  79. 3 1
      src/core/hle/service/am/applets/error.h
  80. 8 6
      src/core/hle/service/am/applets/general_backend.cpp
  81. 8 3
      src/core/hle/service/am/applets/general_backend.h
  82. 2 2
      src/core/hle/service/am/applets/profile_select.cpp
  83. 2 1
      src/core/hle/service/am/applets/profile_select.h
  84. 1015 138
      src/core/hle/service/am/applets/software_keyboard.cpp
  85. 134 53
      src/core/hle/service/am/applets/software_keyboard.h
  86. 295 0
      src/core/hle/service/am/applets/software_keyboard_types.h
  87. 3 2
      src/core/hle/service/am/applets/web_browser.cpp
  88. 3 1
      src/core/hle/service/am/applets/web_browser.h
  89. 4 0
      src/yuzu/CMakeLists.txt
  90. 12 10
      src/yuzu/applets/error.cpp
  91. 1 1
      src/yuzu/applets/error.h
  92. 1600 112
      src/yuzu/applets/software_keyboard.cpp
  93. 246 37
      src/yuzu/applets/software_keyboard.h
  94. 3503 0
      src/yuzu/applets/software_keyboard.ui
  95. 161 24
      src/yuzu/main.cpp
  96. 28 7
      src/yuzu/main.h
  97. 249 0
      src/yuzu/util/overlay_dialog.cpp
  98. 107 0
      src/yuzu/util/overlay_dialog.h
  99. 404 0
      src/yuzu/util/overlay_dialog.ui

BIN
dist/icons/overlay/arrow_left.png


BIN
dist/icons/overlay/arrow_left_dark.png


BIN
dist/icons/overlay/arrow_right.png


BIN
dist/icons/overlay/arrow_right_dark.png


BIN
dist/icons/overlay/button_A.png


BIN
dist/icons/overlay/button_A_dark.png


BIN
dist/icons/overlay/button_B.png


BIN
dist/icons/overlay/button_B_dark.png


BIN
dist/icons/overlay/button_L.png


BIN
dist/icons/overlay/button_L_dark.png


BIN
dist/icons/overlay/button_R.png


BIN
dist/icons/overlay/button_R_dark.png


BIN
dist/icons/overlay/button_X.png


BIN
dist/icons/overlay/button_X_dark.png


BIN
dist/icons/overlay/button_Y.png


BIN
dist/icons/overlay/button_Y_dark.png


BIN
dist/icons/overlay/button_minus.png


BIN
dist/icons/overlay/button_minus_dark.png


BIN
dist/icons/overlay/button_plus.png


BIN
dist/icons/overlay/button_plus_dark.png


BIN
dist/icons/overlay/button_press_stick.png


BIN
dist/icons/overlay/button_press_stick_dark.png


BIN
dist/icons/overlay/controller_dual_joycon.png


BIN
dist/icons/overlay/controller_dual_joycon_dark.png


BIN
dist/icons/overlay/controller_handheld.png


BIN
dist/icons/overlay/controller_handheld_dark.png


BIN
dist/icons/overlay/controller_pro.png


BIN
dist/icons/overlay/controller_pro_dark.png


BIN
dist/icons/overlay/controller_single_joycon_left.png


BIN
dist/icons/overlay/controller_single_joycon_left_a.png


BIN
dist/icons/overlay/controller_single_joycon_left_a_dark.png


BIN
dist/icons/overlay/controller_single_joycon_left_b.png


BIN
dist/icons/overlay/controller_single_joycon_left_b_dark.png


BIN
dist/icons/overlay/controller_single_joycon_left_dark.png


BIN
dist/icons/overlay/controller_single_joycon_left_x.png


BIN
dist/icons/overlay/controller_single_joycon_left_x_dark.png


BIN
dist/icons/overlay/controller_single_joycon_left_y.png


BIN
dist/icons/overlay/controller_single_joycon_left_y_dark.png


BIN
dist/icons/overlay/controller_single_joycon_right.png


BIN
dist/icons/overlay/controller_single_joycon_right_dark.png


BIN
dist/icons/overlay/osk_button_B.png


BIN
dist/icons/overlay/osk_button_B_dark.png


BIN
dist/icons/overlay/osk_button_B_dark_disabled.png


BIN
dist/icons/overlay/osk_button_B_disabled.png


BIN
dist/icons/overlay/osk_button_Y.png


BIN
dist/icons/overlay/osk_button_Y_dark.png


BIN
dist/icons/overlay/osk_button_Y_dark_disabled.png


BIN
dist/icons/overlay/osk_button_Y_disabled.png


BIN
dist/icons/overlay/osk_button_backspace.png


BIN
dist/icons/overlay/osk_button_backspace_dark.png


BIN
dist/icons/overlay/osk_button_plus.png


BIN
dist/icons/overlay/osk_button_plus_dark.png


BIN
dist/icons/overlay/osk_button_plus_dark_disabled.png


BIN
dist/icons/overlay/osk_button_plus_disabled.png


BIN
dist/icons/overlay/osk_button_shift.png


BIN
dist/icons/overlay/osk_button_shift_dark.png


BIN
dist/icons/overlay/osk_button_shift_lock_off.png


BIN
dist/icons/overlay/osk_button_shift_lock_on.png


BIN
dist/icons/overlay/osk_button_shift_on.png


BIN
dist/icons/overlay/osk_button_shift_on_dark.png


+ 64 - 0
dist/icons/overlay/overlay.qrc

@@ -0,0 +1,64 @@
+<RCC>
+  <qresource prefix="overlay">
+    <file>arrow_left.png</file>
+    <file>arrow_left_dark.png</file>
+    <file>arrow_right.png</file>
+    <file>arrow_right_dark.png</file>
+    <file>button_minus.png</file>
+    <file>button_minus_dark.png</file>
+    <file>button_plus.png</file>
+    <file>button_plus_dark.png</file>
+    <file>button_A.png</file>
+    <file>button_A_dark.png</file>
+    <file>button_B.png</file>
+    <file>button_B_dark.png</file>
+    <file>button_X.png</file>
+    <file>button_X_dark.png</file>
+    <file>button_Y.png</file>
+    <file>button_Y_dark.png</file>
+    <file>button_L.png</file>
+    <file>button_L_dark.png</file>
+    <file>button_R.png</file>
+    <file>button_R_dark.png</file>
+    <file>button_press_stick.png</file>
+    <file>button_press_stick_dark.png</file>
+    <file>osk_button_B.png</file>
+    <file>osk_button_B_disabled.png</file>
+    <file>osk_button_B_dark.png</file>
+    <file>osk_button_B_dark_disabled.png</file>
+    <file>osk_button_Y.png</file>
+    <file>osk_button_Y_disabled.png</file>
+    <file>osk_button_Y_dark.png</file>
+    <file>osk_button_Y_dark_disabled.png</file>
+    <file>osk_button_backspace.png</file>
+    <file>osk_button_backspace_dark.png</file>
+    <file>osk_button_plus.png</file>
+    <file>osk_button_plus_disabled.png</file>
+    <file>osk_button_plus_dark.png</file>
+    <file>osk_button_plus_dark_disabled.png</file>
+    <file>osk_button_shift.png</file>
+    <file>osk_button_shift_dark.png</file>
+    <file>osk_button_shift_on.png</file>
+    <file>osk_button_shift_on_dark.png</file>
+    <file>osk_button_shift_lock_on.png</file>
+    <file>osk_button_shift_lock_off.png</file>
+    <file>controller_dual_joycon.png</file>
+    <file>controller_dual_joycon_dark.png</file>
+    <file>controller_pro.png</file>
+    <file>controller_pro_dark.png</file>
+    <file>controller_handheld.png</file>
+    <file>controller_handheld_dark.png</file>
+    <file>controller_single_joycon_left.png</file>
+    <file>controller_single_joycon_left_dark.png</file>
+    <file>controller_single_joycon_right.png</file>
+    <file>controller_single_joycon_right_dark.png</file>
+    <file>controller_single_joycon_left_a.png</file>
+    <file>controller_single_joycon_left_a_dark.png</file>
+    <file>controller_single_joycon_left_b.png</file>
+    <file>controller_single_joycon_left_b_dark.png</file>
+    <file>controller_single_joycon_left_x.png</file>
+    <file>controller_single_joycon_left_x_dark.png</file>
+    <file>controller_single_joycon_left_y.png</file>
+    <file>controller_single_joycon_left_y_dark.png</file>
+  </qresource>
+</RCC>

+ 377 - 0
dist/qt_themes/default/style.qss

@@ -281,3 +281,380 @@ QWidget#controllerPlayer7,
 QWidget#controllerPlayer8 {
 QWidget#controllerPlayer8 {
     background: transparent;
     background: transparent;
 }
 }
+
+QDialog#QtSoftwareKeyboardDialog,
+QStackedWidget#topOSK {
+    background: rgba(51, 51, 51, .9);
+}
+
+
+QDialog#OverlayDialog,
+QStackedWidget#stackedDialog {
+    background: rgba(51, 51, 51, .7);
+}
+
+QWidget#boxOSK,
+QWidget#lineOSK,
+QWidget#richDialog,
+QWidget#lineDialog {
+    background: transparent;
+}
+
+QStackedWidget#bottomOSK,
+QWidget#contentDialog,
+QWidget#contentRichDialog {
+    background: rgba(240, 240, 240, 1);
+}
+
+QWidget#contentDialog,
+QWidget#contentRichDialog {
+    margin: 5px;
+    border-radius: 6px;
+}
+
+QWidget#buttonsDialog,
+QWidget#buttonsRichDialog {
+    margin: 5px;
+    border-top: 2px solid rgba(44, 44, 44, 1);
+}
+
+QWidget#legendOSKnum {
+    border-top: 1px solid rgba(44, 44, 44, 1);
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::vertical {
+    background: #cdcdcd;
+    width: 15px;
+    margin: 15px 3px 15px 3px;
+    border: 1px transparent;
+    border-radius: 4px;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::horizoncal {
+    background: #cdcdcd;
+    height: 15px;
+    margin: 3px 15px 3px 15px;
+    border: 1px transparent;
+    border-radius: 4px;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::handle {
+    background: #fff;
+    border-radius: 4px;
+    min-height: 5px;
+    min-width: 5px;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line,
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line,
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-page,
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-page {
+    background: none;
+}
+
+QWidget#inputOSK {
+    border-bottom: 3px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#inputOSK QLineEdit {
+    background: transparent;
+    border: none;
+    color: #ccc;
+}
+
+QWidget#inputBoxOSK {
+    border: 2px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#inputBoxOSK QTextEdit {
+    background: transparent;
+    border: none;
+    color: #ccc;
+}
+
+QWidget#richDialog QTextBrowser {
+    background: transparent;
+    border: none;
+    padding: 35px 65px;
+}
+
+
+QWidget#lineOSK QLabel#label_header {
+    color: #f0f0f0;
+}
+
+QWidget#lineOSK QLabel#label_sub,
+QWidget#lineOSK QLabel#label_characters,
+QWidget#boxOSK QLabel#label_characters_box {
+    color: #ccc;
+}
+
+QWidget#contentDialog QLabel#label_title,
+QWidget#contentRichDialog QLabel#label_title_rich {
+    color: #888;
+}
+
+QWidget#contentDialog QLabel#label_dialog {
+    padding: 20px 65px;
+}
+
+QWidget#contentDialog QLabel#label_title,
+QWidget#contentRichDialog QLabel#label_title_rich {
+    padding: 0px 65px;
+}
+
+QDialog#OverlayDialog QPushButton {
+    color: rgba(49, 79, 239, 1);
+    background: transparent;
+    border: none;
+    padding: 0px;
+    min-width: 0px;
+}
+
+QDialog#OverlayDialog QPushButton:focus,
+QDialog#OverlayDialog QPushButton:hover {
+    color: rgba(49, 79, 239, 1);
+    background: rgba(255, 255, 255, 1);
+    border: 5px solid rgba(148, 250, 202, 1);
+    border-radius: 6px;
+    outline: none;
+}
+
+QDialog#OverlayDialog QPushButton:pressed {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(150, 150, 150, 1);
+    border: 5px solid rgba(148, 250, 202, 1);
+    border-radius: 6px;
+    outline: none;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton {
+    background: rgba(232, 232, 232, 1);
+    border: 2px solid rgba(240, 240, 240, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
+    background: rgba(218, 218, 218, 1);
+    border: 2px solid rgba(240, 240, 240, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(44, 44, 44, 1);
+    border: 2px solid rgba(240, 240, 240, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(49, 79, 239, 1);
+    border: 2px solid rgba(240, 240, 240, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus,
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover {
+    color: rgba(0, 0, 0, 1);
+    background: rgba(255, 255, 255, 1);
+    border: 5px solid rgba(148, 250, 202, 1);
+    border-radius: 6px;
+    outline: none;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(150, 150, 150, 1);
+    border: 5px solid rgba(148, 250, 202, 1);
+    border-radius: 6px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
+    background-position: right top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_B.png);
+    qproperty-icon: url(:/overlay/osk_button_backspace.png);
+    qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
+    background-position: right top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_Y.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
+    background-position: right top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_plus.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift {
+    background-position: left top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_shift_lock_off.png);
+    qproperty-icon: url(:/overlay/osk_button_shift.png);
+    qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift {
+    background-position: left top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_shift_lock_off.png);
+    qproperty-icon: url(:/overlay/osk_button_shift_on.png);
+    qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis {
+    padding-bottom: 7px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel {
+    background: transparent;
+    color: #ccc;
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num {
+    image: url(:/overlay/button_L.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num {
+    image: url(:/overlay/arrow_left.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num {
+    image: url(:/overlay/button_R.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num {
+    image: url(:/overlay/arrow_right.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift {
+    image: url(:/overlay/button_press_stick.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num {
+    image: url(:/overlay/button_X.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num {
+    image: url(:/overlay/button_A.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
+    color: rgba(164, 164, 164, 1);
+    background-color: rgba(218, 218, 218, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled {
+    color: rgba(164, 164, 164, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled {
+    background-image: url(:/overlay/osk_button_plus_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
+    background-image: url(:/overlay/osk_button_B_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
+    background-image: url(:/overlay/osk_button_Y_disabled.png);
+}

+ 396 - 3
dist/qt_themes/qdarkstyle/style.qss

@@ -1560,7 +1560,400 @@ QWidget#controllerPlayer8 {
     background: transparent;
     background: transparent;
 }
 }
 
 
-/* touchscreen mapping widget */
-TouchScreenPreview {
-    qproperty-dotHighlightColor: #3daee9;
+QDialog#QtSoftwareKeyboardDialog,
+QStackedWidget#topOSK {
+    background: rgba(41, 41, 41, .9);
+}
+
+
+QDialog#OverlayDialog,
+QStackedWidget#stackedDialog {
+    background: rgba(41, 41, 41, .7);
+}
+
+QWidget#boxOSK,
+QWidget#lineOSK,
+QWidget#richDialog,
+QWidget#lineDialog {
+    background: transparent;
+}
+
+QStackedWidget#bottomOSK,
+QWidget#contentDialog,
+QWidget#contentRichDialog {
+    background: rgba(71, 69, 71, 1);
+}
+
+QWidget#contentDialog,
+QWidget#contentRichDialog {
+    margin: 5px;
+    border-radius: 6px;
+}
+
+QWidget#buttonsDialog,
+QWidget#buttonsRichDialog {
+    margin: 5px;
+    border-top: 2px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#legendOSKnum {
+    border-top: 1px solid rgba(255, 255, 255, 1);
+}
+
+QStackedWidget#stackedDialog QTextBrowser QWidget {
+    background: transparent;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar {
+    background: #2a2929;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line,
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line {
+    border-image: none;
+}
+
+QWidget#inputOSK {
+    border-bottom: 3px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#inputOSK QLineEdit {
+    background: transparent;
+    border: none;
+    color: #ccc;
+    padding: 0px;
+}
+
+QWidget#inputBoxOSK {
+    border: 2px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#inputBoxOSK QTextEdit {
+    background: transparent;
+    border: none;
+    color: #ccc;
+}
+
+QWidget#richDialog QTextBrowser {
+    background: transparent;
+    border: none;
+    color: #fff;
+    padding: 35px 65px;
+}
+
+QWidget#lineOSK QLabel#label_header {
+    color: #f0f0f0;
+}
+
+QWidget#lineOSK QLabel#label_sub,
+QWidget#lineOSK QLabel#label_characters,
+QWidget#contentDialog QLabel#label_title,
+QWidget#contentRichDialog QLabel#label_title_rich,
+QWidget#boxOSK QLabel#label_characters_box {
+    color: #ccc;
+}
+
+QWidget#buttonsDialog,
+QWidget#buttonsRichDialog,
+QWidget#mainOSK,
+QWidget#headerOSK,
+QWidget#normalOSK,
+QWidget#shiftOSK,
+QWidget#numOSK,
+QWidget#subOSK,
+QWidget#inputOSK,
+QWidget#inputBoxOSK,
+QWidget#charactersOSK,
+QWidget#charactersBoxOSK,
+QWidget#legendOSK,
+QWidget#legendOSK QWidget,
+QWidget#legendOSKshift,
+QWidget#legendOSKshift QWidget,
+QWidget#legendOSKnum,
+QWidget#legendOSKnum QWidget {
+    background: transparent;
+}
+
+QWidget#contentDialog QLabel,
+QWidget#legendOSK QLabel,
+QWidget#legendOSKshift QLabel,
+QWidget#legendOSKnum QLabel {
+    color: rgba(255, 255, 255, 1);
+}
+
+QWidget#contentDialog QLabel#label_dialog {
+    padding: 20px 65px;
+}
+
+QWidget#contentDialog QLabel#label_title,
+QWidget#contentRichDialog QLabel#label_title_rich {
+    padding: 0px 65px;
+}
+
+QDialog#OverlayDialog QPushButton {
+    color: rgba(1, 253, 201, 1);
+    background: transparent;
+    border: none;
+    padding: 0px;
+    min-width: 0px;
+}
+
+QDialog#OverlayDialog QPushButton:focus,
+QDialog#OverlayDialog QPushButton:hover {
+    color: rgba(1, 253, 201, 1);
+    background: rgba(58, 61, 66, 1);
+    border: 5px solid rgba(56, 189, 225, 1);
+    border-radius: 6px;
+    outline: none;
+}
+
+QDialog#OverlayDialog QPushButton:pressed {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(150, 150, 150, 1);
+    border: 5px solid rgba(56, 189, 225, 1);
+    border-radius: 6px;
+    outline: none;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton {
+    color: rgba(255, 255, 255, 1);
+    background: rgba(80, 79, 80, 1);
+    border: 2px solid rgba(71, 69, 71, 1);
+    padding: 0px;
+    min-width: 0px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
+    background: rgba(95, 94, 95, 1);
+    border: 2px solid rgba(71, 69, 71, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(255, 255, 255, 1);
+    border: 2px solid rgba(71, 69, 71, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
+    color: rgba(0, 0, 0, 1);
+    background: rgba(1, 253, 201, 1);
+    border: 2px solid rgba(71, 69, 71, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus,
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover {
+    color: rgba(255, 255, 255, 1);
+    background: rgba(58, 61, 66, 1);
+    border: 5px solid rgba(56, 189, 225, 1);
+    border-radius: 6px;
+    outline: none;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed {
+    color: rgba(240, 240, 240, 1);
+    background: rgba(150, 150, 150, 1);
+    border: 5px solid rgba(56, 189, 225, 1);
+    border-radius: 6px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
+    background-position: right top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_B_dark.png);
+    qproperty-icon: url(:/overlay/osk_button_backspace_dark.png);
+    qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
+    background-position: right top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_Y_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
+    color: rgba(44, 44, 44, 1);
+    background-position: right top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_plus_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift {
+    background-position: left top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_shift_lock_off.png);
+    qproperty-icon: url(:/overlay/osk_button_shift_dark.png);
+    qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift {
+    background-position: left top;
+    background-repeat: no-repeat;
+    background-origin: content;
+    background-image: url(:/overlay/osk_button_shift_lock_off.png);
+    qproperty-icon: url(:/overlay/osk_button_shift_on_dark.png);
+    qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis {
+    padding-bottom: 7px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel {
+    background: transparent;
+    color: #ccc;
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num {
+    image: url(:/overlay/button_L_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num {
+    image: url(:/overlay/arrow_left_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num {
+    image: url(:/overlay/button_R_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num {
+    image: url(:/overlay/arrow_right_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift {
+    image: url(:/overlay/button_press_stick_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num {
+    image: url(:/overlay/button_X_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num {
+    image: url(:/overlay/button_A_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
+    color: rgba(144, 144, 144, 1);
+    background-color: rgba(95, 94, 95, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled {
+    color: rgba(144, 144, 144, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled {
+    background-image: url(:/overlay/osk_button_plus_dark_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
+    background-image: url(:/overlay/osk_button_B_dark_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
+    background-image: url(:/overlay/osk_button_Y_dark_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QFrame,
+QDialog#QtSoftwareKeyboardDialog QFrame[frameShape="0"],
+QDialog#OverlayDialog QFrame,
+QDialog#OverlayDialog QFrame[frameShape="0"] {
+    border-radius: 0px;
+    border: none;
 }
 }

+ 420 - 19
dist/qt_themes/qdarkstyle_midnight_blue/style.qss

@@ -1,10 +1,10 @@
 /* ---------------------------------------------------------------------------
 /* ---------------------------------------------------------------------------
 
 
-    Created by the qtsass compiler v0.1.1
+  Created by the qtsass compiler v0.1.1
 
 
-    The definitions are in the "qdarkstyle.qss._styles.scss" module
+  The definitions are in the "qdarkstyle.qss._styles.scss" module
 
 
-    WARNING! All changes made in this file will be lost!
+  WARNING! All changes made in this file will be lost!
 
 
 --------------------------------------------------------------------------- */
 --------------------------------------------------------------------------- */
 /* QDarkStyleSheet -----------------------------------------------------------
 /* QDarkStyleSheet -----------------------------------------------------------
@@ -15,34 +15,34 @@ It is based on three selecting colors, three greyish (background) colors
 plus three whitish (foreground) colors. Each set of widgets of the same
 plus three whitish (foreground) colors. Each set of widgets of the same
 type have a header like this:
 type have a header like this:
 
 
-    ------------------
-    GroupName --------
-    ------------------
+  ------------------
+  GroupName --------
+  ------------------
 
 
 And each widget is separated with a header like this:
 And each widget is separated with a header like this:
 
 
-    QWidgetName ------
+  QWidgetName ------
 
 
 This makes more easy to find and change some css field. The basic
 This makes more easy to find and change some css field. The basic
 configuration is described bellow.
 configuration is described bellow.
 
 
-    BACKGROUND -----------
+  BACKGROUND -----------
 
 
-        Light   (unpressed)
-        Normal  (border, disabled, pressed, checked, toolbars, menus)
-        Dark    (background)
+    Light   (unpressed)
+    Normal  (border, disabled, pressed, checked, toolbars, menus)
+    Dark    (background)
 
 
-    FOREGROUND -----------
+  FOREGROUND -----------
 
 
-        Light   (texts/labels)
-        Normal  (not used yet)
-        Dark    (disabled texts)
+    Light   (texts/labels)
+    Normal  (not used yet)
+    Dark    (disabled texts)
 
 
-    SELECTION ------------
+  SELECTION ------------
 
 
-        Light  (selection/hover/active)
-        Normal (selected)
-        Dark   (selected disabled)
+    Light  (selection/hover/active)
+    Normal (selected)
+    Dark   (selected disabled)
 
 
 If a stranger configuration is required because of a bugfix or anything
 If a stranger configuration is required because of a bugfix or anything
 else, keep the comment on the line above so nobody changes it, including the
 else, keep the comment on the line above so nobody changes it, including the
@@ -2483,3 +2483,404 @@ QWidget#controllerPlayer7,
 QWidget#controllerPlayer8 {
 QWidget#controllerPlayer8 {
   background: transparent;
   background: transparent;
 }
 }
+
+QDialog#QtSoftwareKeyboardDialog,
+QStackedWidget#topOSK {
+  background: rgba(15, 25, 34, .9);
+}
+
+QDialog#OverlayDialog,
+QStackedWidget#stackedDialog {
+  background: rgba(15, 25, 34, .7);
+}
+
+QWidget#boxOSK,
+QWidget#lineOSK,
+QWidget#richDialog,
+QWidget#lineDialog {
+  background: transparent;
+}
+
+QStackedWidget#bottomOSK,
+QWidget#contentDialog,
+QWidget#contentRichDialog {
+  background: rgba(31, 41, 51, 1);
+}
+
+QWidget#contentDialog,
+QWidget#contentRichDialog {
+  margin: 5px;
+  border-radius: 6px;
+}
+
+QWidget#buttonsDialog,
+QWidget#buttonsRichDialog {
+  margin: 5px;
+  border-top: 2px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#legendOSKnum {
+  border-top: 1px solid rgba(255, 255, 255, 1);
+}
+
+QStackedWidget#stackedDialog QTextBrowser QWidget {
+  background: transparent;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar {
+  background: #19232d;
+  border: none;
+}
+
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::sub-line,
+QStackedWidget#stackedDialog QTextBrowser QScrollBar::add-line {
+  border-image: none;
+}
+
+QWidget#mainOSK QStackedWidget,
+QDialog#OverlayDialog QStackedWidget {
+  border: none;
+  padding: 0px;
+}
+
+QWidget#inputOSK {
+  border-bottom: 3px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#inputOSK QLineEdit {
+  background: transparent;
+  border: none;
+  color: #ccc;
+  padding: 0px;
+}
+
+QWidget#inputBoxOSK {
+  border: 2px solid rgba(255, 255, 255, .9);
+}
+
+QWidget#inputBoxOSK QTextEdit {
+  background: transparent;
+  border: none;
+  color: #ccc;
+}
+
+QWidget#richDialog QTextBrowser {
+  background: transparent;
+  border: none;
+  color: #fff;
+  padding: 35px 65px;
+}
+
+QWidget#lineOSK QLabel#label_header {
+  color: #f0f0f0;
+}
+
+QWidget#lineOSK QLabel#label_sub,
+QWidget#lineOSK QLabel#label_characters,
+QWidget#contentDialog QLabel#label_title,
+QWidget#contentRichDialog QLabel#label_title_rich,
+QWidget#boxOSK QLabel#label_characters_box {
+  color: #ccc;
+}
+
+QWidget#buttonsDialog,
+QWidget#buttonsRichDialog,
+QWidget#mainOSK,
+QWidget#headerOSK,
+QWidget#normalOSK,
+QWidget#shiftOSK,
+QWidget#numOSK,
+QWidget#subOSK,
+QWidget#inputOSK,
+QWidget#inputBoxOSK,
+QWidget#charactersOSK,
+QWidget#charactersBoxOSK,
+QWidget#legendOSK,
+QWidget#legendOSK QWidget,
+QWidget#legendOSKshift,
+QWidget#legendOSKshift QWidget,
+QWidget#legendOSKnum,
+QWidget#legendOSKnum QWidget {
+  background: transparent;
+}
+
+QWidget#contentDialog QLabel,
+QWidget#legendOSK QLabel,
+QWidget#legendOSKshift QLabel,
+QWidget#legendOSKnum QLabel {
+  color: rgba(255, 255, 255, 1);
+}
+
+QWidget#contentDialog QLabel#label_dialog {
+  padding: 20px 65px;
+}
+
+QWidget#contentDialog QLabel#label_title,
+QWidget#contentRichDialog QLabel#label_title_rich {
+  padding: 0px 65px;
+}
+
+QDialog#OverlayDialog QPushButton {
+  color: rgba(1, 253, 201, 1);
+  background: transparent;
+  border: none;
+  padding: 0px;
+  min-width: 0px;
+}
+
+QDialog#OverlayDialog QPushButton:focus,
+QDialog#OverlayDialog QPushButton:hover {
+  color: rgba(1, 253, 201, 1);
+  background: rgba(18, 33, 46, 1);
+  border: 5px solid rgba(56, 189, 225, 1);
+  border-radius: 6px;
+  outline: none;
+}
+
+QDialog#OverlayDialog QPushButton:pressed {
+  color: rgba(240, 240, 240, 1);
+  background: rgba(110, 122, 130, 1);
+  border: 5px solid rgba(56, 189, 225, 1);
+  border-radius: 6px;
+  outline: none;
+}
+
+QDialog#QtSoftwareKeyboardDialog QLabel {
+  padding: 0px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton {
+  color: rgba(255, 255, 255, 1);
+  background: rgba(40, 51, 60, 1);
+  border: 2px solid rgba(31, 41, 51, 1);
+  border-radius: 0px;
+  padding: 0px;
+  min-width: 0px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
+  background: rgba(55, 66, 75, 1);
+  border: 2px solid rgba(31, 41, 51, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
+  color: rgba(240, 240, 240, 1);
+  background: rgba(255, 255, 255, 1);
+  border: 2px solid rgba(31, 41, 51, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
+  color: rgba(0, 0, 0, 1);
+  background: rgba(1, 253, 201, 1);
+  border: 2px solid rgba(31, 41, 51, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:focus,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:focus,
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:hover,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:hover {
+  color: rgba(255, 255, 255, 1);
+  background: rgba(18, 33, 46, 1);
+  border: 5px solid rgba(56, 189, 225, 1);
+  border-radius: 6px;
+  outline: none;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:pressed,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:pressed {
+  color: rgba(240, 240, 240, 1);
+  background: rgba(110, 122, 130, 1);
+  border: 5px solid rgba(56, 189, 225, 1);
+  border-radius: 6px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num {
+  background-position: right top;
+  background-repeat: no-repeat;
+  background-origin: content;
+  background-image: url(:/overlay/osk_button_B_dark.png);
+  qproperty-icon: url(:/overlay/osk_button_backspace_dark.png);
+  qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift {
+  background-position: right top;
+  background-repeat: no-repeat;
+  background-origin: content;
+  background-image: url(:/overlay/osk_button_Y_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num {
+  color: rgba(44, 44, 44, 1);
+  background-position: right top;
+  background-repeat: no-repeat;
+  background-origin: content;
+  background-image: url(:/overlay/osk_button_plus_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift {
+  background-position: left top;
+  background-repeat: no-repeat;
+  background-origin: content;
+  background-image: url(:/overlay/osk_button_shift_lock_off.png);
+  qproperty-icon: url(:/overlay/osk_button_shift_dark.png);
+  qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_shift_shift {
+  background-position: left top;
+  background-repeat: no-repeat;
+  background-origin: content;
+  background-image: url(:/overlay/osk_button_shift_lock_off.png);
+  qproperty-icon: url(:/overlay/osk_button_shift_on_dark.png);
+  qproperty-iconSize: 36px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_bracket,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_bracket,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_left_parenthesis,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_right_parenthesis {
+  padding-bottom: 7px;
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#titleOSK QLabel {
+  background: transparent;
+  color: #ccc;
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_L_num {
+  image: url(:/overlay/button_L_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_left_num {
+  image: url(:/overlay/arrow_left_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_R_num {
+  image: url(:/overlay/button_R_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#arrow_right_num {
+  image: url(:/overlay/arrow_right_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_press_stick_shift {
+  image: url(:/overlay/button_press_stick_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_X_num {
+  image: url(:/overlay/button_X_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A_shift,
+QDialog#QtSoftwareKeyboardDialog QWidget#button_A_num {
+  image: url(:/overlay/button_A_dark.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
+  color: rgba(144, 144, 144, 1);
+  background-color: rgba(55, 66, 75, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_at:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_slash:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_percent:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_1:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_2:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_3:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_4:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_5:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_6:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_7:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_8:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_9:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_0:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_return:disabled {
+  color: rgba(144, 144, 144, 1);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_ok_num:disabled {
+  background-image: url(:/overlay/osk_button_plus_dark_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_shift:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_backspace_num:disabled {
+  background-image: url(:/overlay/osk_button_B_dark_disabled.png);
+}
+
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space:disabled,
+QDialog#QtSoftwareKeyboardDialog QPushButton#button_space_shift:disabled {
+  background-image: url(:/overlay/osk_button_Y_dark_disabled.png);
+}

+ 1 - 0
src/core/CMakeLists.txt

@@ -273,6 +273,7 @@ add_library(core STATIC
     hle/service/am/applets/profile_select.h
     hle/service/am/applets/profile_select.h
     hle/service/am/applets/software_keyboard.cpp
     hle/service/am/applets/software_keyboard.cpp
     hle/service/am/applets/software_keyboard.h
     hle/service/am/applets/software_keyboard.h
+    hle/service/am/applets/software_keyboard_types.h
     hle/service/am/applets/web_browser.cpp
     hle/service/am/applets/web_browser.cpp
     hle/service/am/applets/web_browser.h
     hle/service/am/applets/web_browser.h
     hle/service/am/applets/web_types.h
     hle/service/am/applets/web_types.h

+ 134 - 14
src/core/frontend/applets/software_keyboard.cpp

@@ -1,29 +1,149 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2021 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
-#include "common/logging/backend.h"
+#include <thread>
+
+#include "common/logging/log.h"
 #include "common/string_util.h"
 #include "common/string_util.h"
 #include "core/frontend/applets/software_keyboard.h"
 #include "core/frontend/applets/software_keyboard.h"
 
 
 namespace Core::Frontend {
 namespace Core::Frontend {
+
 SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
 SoftwareKeyboardApplet::~SoftwareKeyboardApplet() = default;
 
 
-void DefaultSoftwareKeyboardApplet::RequestText(
-    std::function<void(std::optional<std::u16string>)> out,
-    SoftwareKeyboardParameters parameters) const {
-    if (parameters.initial_text.empty())
-        out(u"yuzu");
+DefaultSoftwareKeyboardApplet::~DefaultSoftwareKeyboardApplet() = default;
+
+void DefaultSoftwareKeyboardApplet::InitializeKeyboard(
+    bool is_inline, 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) {
+        LOG_WARNING(
+            Service_AM,
+            "(STUBBED) called, backend requested to initialize the inline software keyboard.");
+
+        submit_inline_callback = std::move(submit_inline_callback_);
+    } else {
+        LOG_WARNING(
+            Service_AM,
+            "(STUBBED) called, backend requested to initialize the normal software keyboard.");
+
+        submit_normal_callback = std::move(submit_normal_callback_);
+    }
+
+    parameters = std::move(initialize_parameters);
+
+    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(parameters.ok_text), Common::UTF16ToUTF8(parameters.header_text),
+             Common::UTF16ToUTF8(parameters.sub_text), Common::UTF16ToUTF8(parameters.guide_text),
+             Common::UTF16ToUTF8(parameters.initial_text), parameters.max_text_length,
+             parameters.min_text_length, parameters.initial_cursor_position, parameters.type,
+             parameters.password_mode, parameters.text_draw_type, parameters.key_disable_flags.raw,
+             parameters.use_blur_background, parameters.enable_backspace_button,
+             parameters.enable_return_button, parameters.disable_cancel_button);
+}
+
+void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const {
+    LOG_WARNING(Service_AM,
+                "(STUBBED) called, backend requested to show the normal software keyboard.");
+
+    SubmitNormalText(u"yuzu");
+}
+
+void DefaultSoftwareKeyboardApplet::ShowTextCheckDialog(
+    Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+    std::u16string text_check_message) const {
+    LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to show the text check dialog.");
+}
+
+void DefaultSoftwareKeyboardApplet::ShowInlineKeyboard(
+    InlineAppearParameters appear_parameters) const {
+    LOG_WARNING(Service_AM,
+                "(STUBBED) called, backend requested to show the inline software keyboard.");
+
+    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);
+
+    std::thread([this] { SubmitInlineText(u"yuzu"); }).detach();
+}
 
 
-    out(parameters.initial_text);
+void DefaultSoftwareKeyboardApplet::HideInlineKeyboard() const {
+    LOG_WARNING(Service_AM,
+                "(STUBBED) called, backend requested to hide the inline software keyboard.");
 }
 }
 
 
-void DefaultSoftwareKeyboardApplet::SendTextCheckDialog(
-    std::u16string error_message, std::function<void()> finished_check) const {
+void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_parameters) const {
     LOG_WARNING(Service_AM,
     LOG_WARNING(Service_AM,
-                "(STUBBED) called - Default fallback software keyboard does not support text "
-                "check! (error_message={})",
-                Common::UTF16ToUTF8(error_message));
-    finished_check();
+                "(STUBBED) called, backend requested to change the inline keyboard text.");
+
+    LOG_INFO(Service_AM,
+             "\nInlineTextParameters:"
+             "\ninput_text={}"
+             "\ncursor_position={}",
+             Common::UTF16ToUTF8(text_parameters.input_text), text_parameters.cursor_position);
+
+    submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
+                           text_parameters.input_text, text_parameters.cursor_position);
+}
+
+void DefaultSoftwareKeyboardApplet::ExitKeyboard() const {
+    LOG_WARNING(Service_AM, "(STUBBED) called, backend requested to exit the software keyboard.");
 }
 }
+
+void DefaultSoftwareKeyboardApplet::SubmitNormalText(std::u16string text) const {
+    submit_normal_callback(Service::AM::Applets::SwkbdResult::Ok, text);
+}
+
+void DefaultSoftwareKeyboardApplet::SubmitInlineText(std::u16string_view text) const {
+    std::this_thread::sleep_for(std::chrono::milliseconds(500));
+
+    for (std::size_t index = 0; index < text.size(); ++index) {
+        submit_inline_callback(Service::AM::Applets::SwkbdReplyType::ChangedString,
+                               std::u16string(text.data(), text.data() + index + 1),
+                               static_cast<s32>(index) + 1);
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(250));
+    }
+
+    submit_inline_callback(Service::AM::Applets::SwkbdReplyType::DecidedEnter, std::u16string(text),
+                           static_cast<s32>(text.size()));
+}
+
 } // namespace Core::Frontend
 } // namespace Core::Frontend

+ 90 - 28
src/core/frontend/applets/software_keyboard.h

@@ -1,54 +1,116 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2021 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
 #pragma once
 #pragma once
 
 
 #include <functional>
 #include <functional>
-#include <optional>
-#include <string>
-#include "common/bit_field.h"
+#include <thread>
+
 #include "common/common_types.h"
 #include "common/common_types.h"
 
 
+#include "core/hle/service/am/applets/software_keyboard_types.h"
+
 namespace Core::Frontend {
 namespace Core::Frontend {
-struct SoftwareKeyboardParameters {
-    std::u16string submit_text;
+
+struct KeyboardInitializeParameters {
+    std::u16string ok_text;
     std::u16string header_text;
     std::u16string header_text;
     std::u16string sub_text;
     std::u16string sub_text;
     std::u16string guide_text;
     std::u16string guide_text;
     std::u16string initial_text;
     std::u16string initial_text;
-    std::size_t max_length;
-    bool password;
-    bool cursor_at_beginning;
-
-    union {
-        u8 value;
-
-        BitField<1, 1, u8> disable_space;
-        BitField<2, 1, u8> disable_address;
-        BitField<3, 1, u8> disable_percent;
-        BitField<4, 1, u8> disable_slash;
-        BitField<6, 1, u8> disable_number;
-        BitField<7, 1, u8> disable_download_code;
-    };
+    u32 max_text_length;
+    u32 min_text_length;
+    s32 initial_cursor_position;
+    Service::AM::Applets::SwkbdType type;
+    Service::AM::Applets::SwkbdPasswordMode password_mode;
+    Service::AM::Applets::SwkbdTextDrawType text_draw_type;
+    Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags;
+    bool use_blur_background;
+    bool enable_backspace_button;
+    bool enable_return_button;
+    bool disable_cancel_button;
+};
+
+struct InlineAppearParameters {
+    u32 max_text_length;
+    u32 min_text_length;
+    f32 key_top_scale_x;
+    f32 key_top_scale_y;
+    f32 key_top_translate_x;
+    f32 key_top_translate_y;
+    Service::AM::Applets::SwkbdType type;
+    Service::AM::Applets::SwkbdKeyDisableFlags key_disable_flags;
+    bool key_top_as_floating;
+    bool enable_backspace_button;
+    bool enable_return_button;
+    bool disable_cancel_button;
+};
+
+struct InlineTextParameters {
+    std::u16string input_text;
+    s32 cursor_position;
 };
 };
 
 
 class SoftwareKeyboardApplet {
 class SoftwareKeyboardApplet {
 public:
 public:
     virtual ~SoftwareKeyboardApplet();
     virtual ~SoftwareKeyboardApplet();
 
 
-    virtual void RequestText(std::function<void(std::optional<std::u16string>)> out,
-                             SoftwareKeyboardParameters parameters) const = 0;
-    virtual void SendTextCheckDialog(std::u16string error_message,
-                                     std::function<void()> finished_check) const = 0;
+    virtual void InitializeKeyboard(
+        bool is_inline, 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_) = 0;
+
+    virtual void ShowNormalKeyboard() const = 0;
+
+    virtual void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+                                     std::u16string text_check_message) const = 0;
+
+    virtual void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const = 0;
+
+    virtual void HideInlineKeyboard() const = 0;
+
+    virtual void InlineTextChanged(InlineTextParameters text_parameters) const = 0;
+
+    virtual void ExitKeyboard() const = 0;
 };
 };
 
 
 class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
 class DefaultSoftwareKeyboardApplet final : public SoftwareKeyboardApplet {
 public:
 public:
-    void RequestText(std::function<void(std::optional<std::u16string>)> out,
-                     SoftwareKeyboardParameters parameters) const override;
-    void SendTextCheckDialog(std::u16string error_message,
-                             std::function<void()> finished_check) const override;
+    ~DefaultSoftwareKeyboardApplet() override;
+
+    void InitializeKeyboard(
+        bool is_inline, 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_) override;
+
+    void ShowNormalKeyboard() const override;
+
+    void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+                             std::u16string text_check_message) const override;
+
+    void ShowInlineKeyboard(InlineAppearParameters appear_parameters) const override;
+
+    void HideInlineKeyboard() const override;
+
+    void InlineTextChanged(InlineTextParameters text_parameters) const override;
+
+    void ExitKeyboard() const override;
+
+private:
+    void SubmitNormalText(std::u16string text) const;
+    void SubmitInlineText(std::u16string_view text) const;
+
+    KeyboardInitializeParameters parameters;
+
+    mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
+        submit_normal_callback;
+    mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
+        submit_inline_callback;
 };
 };
 
 
 } // namespace Core::Frontend
 } // namespace Core::Frontend

+ 14 - 1
src/core/frontend/input_interpreter.cpp

@@ -12,7 +12,9 @@ InputInterpreter::InputInterpreter(Core::System& system)
     : npad{system.ServiceManager()
     : npad{system.ServiceManager()
                .GetService<Service::HID::Hid>("hid")
                .GetService<Service::HID::Hid>("hid")
                ->GetAppletResource()
                ->GetAppletResource()
-               ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {}
+               ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad)} {
+    ResetButtonStates();
+}
 
 
 InputInterpreter::~InputInterpreter() = default;
 InputInterpreter::~InputInterpreter() = default;
 
 
@@ -25,6 +27,17 @@ void InputInterpreter::PollInput() {
     button_states[current_index] = button_state;
     button_states[current_index] = button_state;
 }
 }
 
 
+void InputInterpreter::ResetButtonStates() {
+    previous_index = 0;
+    current_index = 0;
+
+    button_states[0] = 0xFFFFFFFF;
+
+    for (std::size_t i = 1; i < button_states.size(); ++i) {
+        button_states[i] = 0;
+    }
+}
+
 bool InputInterpreter::IsButtonPressed(HIDButton button) const {
 bool InputInterpreter::IsButtonPressed(HIDButton button) const {
     return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
     return (button_states[current_index] & (1U << static_cast<u8>(button))) != 0;
 }
 }

+ 3 - 0
src/core/frontend/input_interpreter.h

@@ -66,6 +66,9 @@ public:
     /// Gets a button state from HID and inserts it into the array of button states.
     /// Gets a button state from HID and inserts it into the array of button states.
     void PollInput();
     void PollInput();
 
 
+    /// Resets all the button states to their defaults.
+    void ResetButtonStates();
+
     /**
     /**
      * Checks whether the button is pressed.
      * Checks whether the button is pressed.
      *
      *

+ 6 - 2
src/core/hle/kernel/hle_ipc.cpp

@@ -75,10 +75,14 @@ void HLERequestContext::ParseCommandBuffer(const HandleTable& handle_table, u32_
         if (incoming) {
         if (incoming) {
             // Populate the object lists with the data in the IPC request.
             // Populate the object lists with the data in the IPC request.
             for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
             for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
-                copy_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
+                const u32 copy_handle{rp.Pop<Handle>()};
+                copy_handles.push_back(copy_handle);
+                copy_objects.push_back(handle_table.GetGeneric(copy_handle));
             }
             }
             for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
             for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_move; ++handle) {
-                move_objects.push_back(handle_table.GetGeneric(rp.Pop<Handle>()));
+                const u32 move_handle{rp.Pop<Handle>()};
+                move_handles.push_back(move_handle);
+                move_objects.push_back(handle_table.GetGeneric(move_handle));
             }
             }
         } else {
         } else {
             // For responses we just ignore the handles, they're empty and will be populated when
             // For responses we just ignore the handles, they're empty and will be populated when

+ 10 - 0
src/core/hle/kernel/hle_ipc.h

@@ -210,6 +210,14 @@ public:
     /// Helper function to test whether the output buffer at buffer_index can be written
     /// Helper function to test whether the output buffer at buffer_index can be written
     bool CanWriteBuffer(std::size_t buffer_index = 0) const;
     bool CanWriteBuffer(std::size_t buffer_index = 0) const;
 
 
+    Handle GetCopyHandle(std::size_t index) const {
+        return copy_handles.at(index);
+    }
+
+    Handle GetMoveHandle(std::size_t index) const {
+        return move_handles.at(index);
+    }
+
     template <typename T>
     template <typename T>
     std::shared_ptr<T> GetCopyObject(std::size_t index) {
     std::shared_ptr<T> GetCopyObject(std::size_t index) {
         return DynamicObjectCast<T>(copy_objects.at(index));
         return DynamicObjectCast<T>(copy_objects.at(index));
@@ -285,6 +293,8 @@ private:
     std::shared_ptr<Kernel::ServerSession> server_session;
     std::shared_ptr<Kernel::ServerSession> server_session;
     std::shared_ptr<KThread> thread;
     std::shared_ptr<KThread> thread;
     // TODO(yuriks): Check common usage of this and optimize size accordingly
     // TODO(yuriks): Check common usage of this and optimize size accordingly
+    boost::container::small_vector<Handle, 8> move_handles;
+    boost::container::small_vector<Handle, 8> copy_handles;
     boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
     boost::container::small_vector<std::shared_ptr<Object>, 8> move_objects;
     boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
     boost::container::small_vector<std::shared_ptr<Object>, 8> copy_objects;
     boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;
     boost::container::small_vector<std::shared_ptr<SessionRequestHandler>, 8> domain_objects;

+ 67 - 10
src/core/hle/service/am/am.cpp

@@ -971,7 +971,7 @@ private:
 
 
         auto storage = applet->GetBroker().PopNormalDataToGame();
         auto storage = applet->GetBroker().PopNormalDataToGame();
         if (storage == nullptr) {
         if (storage == nullptr) {
-            LOG_ERROR(Service_AM,
+            LOG_DEBUG(Service_AM,
                       "storage is a nullptr. There is no data in the current normal channel");
                       "storage is a nullptr. There is no data in the current normal channel");
             IPC::ResponseBuilder rb{ctx, 2};
             IPC::ResponseBuilder rb{ctx, 2};
             rb.Push(ERR_NO_DATA_IN_CHANNEL);
             rb.Push(ERR_NO_DATA_IN_CHANNEL);
@@ -1002,7 +1002,7 @@ private:
 
 
         auto storage = applet->GetBroker().PopInteractiveDataToGame();
         auto storage = applet->GetBroker().PopInteractiveDataToGame();
         if (storage == nullptr) {
         if (storage == nullptr) {
-            LOG_ERROR(Service_AM,
+            LOG_DEBUG(Service_AM,
                       "storage is a nullptr. There is no data in the current interactive channel");
                       "storage is a nullptr. There is no data in the current interactive channel");
             IPC::ResponseBuilder rb{ctx, 2};
             IPC::ResponseBuilder rb{ctx, 2};
             rb.Push(ERR_NO_DATA_IN_CHANNEL);
             rb.Push(ERR_NO_DATA_IN_CHANNEL);
@@ -1125,7 +1125,7 @@ ILibraryAppletCreator::ILibraryAppletCreator(Core::System& system_)
         {2, nullptr, "AreAnyLibraryAppletsLeft"},
         {2, nullptr, "AreAnyLibraryAppletsLeft"},
         {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
         {10, &ILibraryAppletCreator::CreateStorage, "CreateStorage"},
         {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
         {11, &ILibraryAppletCreator::CreateTransferMemoryStorage, "CreateTransferMemoryStorage"},
-        {12, nullptr, "CreateHandleStorage"},
+        {12, &ILibraryAppletCreator::CreateHandleStorage, "CreateHandleStorage"},
     };
     };
     RegisterHandlers(functions);
     RegisterHandlers(functions);
 }
 }
@@ -1134,14 +1134,15 @@ ILibraryAppletCreator::~ILibraryAppletCreator() = default;
 
 
 void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
 void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     IPC::RequestParser rp{ctx};
+
     const auto applet_id = rp.PopRaw<Applets::AppletId>();
     const auto applet_id = rp.PopRaw<Applets::AppletId>();
-    const auto applet_mode = rp.PopRaw<u32>();
+    const auto applet_mode = rp.PopRaw<Applets::LibraryAppletMode>();
 
 
     LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
     LOG_DEBUG(Service_AM, "called with applet_id={:08X}, applet_mode={:08X}", applet_id,
               applet_mode);
               applet_mode);
 
 
     const auto& applet_manager{system.GetAppletManager()};
     const auto& applet_manager{system.GetAppletManager()};
-    const auto applet = applet_manager.GetApplet(applet_id);
+    const auto applet = applet_manager.GetApplet(applet_id, applet_mode);
 
 
     if (applet == nullptr) {
     if (applet == nullptr) {
         LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
         LOG_ERROR(Service_AM, "Applet doesn't exist! applet_id={}", applet_id);
@@ -1159,9 +1160,18 @@ void ILibraryAppletCreator::CreateLibraryApplet(Kernel::HLERequestContext& ctx)
 
 
 void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
 void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     IPC::RequestParser rp{ctx};
-    const u64 size{rp.Pop<u64>()};
+
+    const s64 size{rp.Pop<s64>()};
+
     LOG_DEBUG(Service_AM, "called, size={}", size);
     LOG_DEBUG(Service_AM, "called, size={}", size);
 
 
+    if (size <= 0) {
+        LOG_ERROR(Service_AM, "size is less than or equal to 0");
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(RESULT_UNKNOWN);
+        return;
+    }
+
     std::vector<u8> buffer(size);
     std::vector<u8> buffer(size);
 
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
@@ -1170,18 +1180,65 @@ void ILibraryAppletCreator::CreateStorage(Kernel::HLERequestContext& ctx) {
 }
 }
 
 
 void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
 void ILibraryAppletCreator::CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx) {
-    LOG_DEBUG(Service_AM, "called");
+    IPC::RequestParser rp{ctx};
+
+    struct Parameters {
+        u8 permissions;
+        s64 size;
+    };
+
+    const auto parameters{rp.PopRaw<Parameters>()};
+    const auto handle{ctx.GetCopyHandle(0)};
+
+    LOG_DEBUG(Service_AM, "called, permissions={}, size={}, handle={:08X}", parameters.permissions,
+              parameters.size, handle);
+
+    if (parameters.size <= 0) {
+        LOG_ERROR(Service_AM, "size is less than or equal to 0");
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(RESULT_UNKNOWN);
+        return;
+    }
+
+    auto transfer_mem =
+        system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
+
+    if (transfer_mem == nullptr) {
+        LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(RESULT_UNKNOWN);
+        return;
+    }
+
+    const u8* const mem_begin = transfer_mem->GetPointer();
+    const u8* const mem_end = mem_begin + transfer_mem->GetSize();
+    std::vector<u8> memory{mem_begin, mem_end};
 
 
+    IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+    rb.Push(RESULT_SUCCESS);
+    rb.PushIpcInterface<IStorage>(system, std::move(memory));
+}
+
+void ILibraryAppletCreator::CreateHandleStorage(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     IPC::RequestParser rp{ctx};
 
 
-    rp.SetCurrentOffset(3);
-    const auto handle{rp.Pop<Kernel::Handle>()};
+    const s64 size{rp.Pop<s64>()};
+    const auto handle{ctx.GetCopyHandle(0)};
+
+    LOG_DEBUG(Service_AM, "called, size={}, handle={:08X}", size, handle);
+
+    if (size <= 0) {
+        LOG_ERROR(Service_AM, "size is less than or equal to 0");
+        IPC::ResponseBuilder rb{ctx, 2};
+        rb.Push(RESULT_UNKNOWN);
+        return;
+    }
 
 
     auto transfer_mem =
     auto transfer_mem =
         system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
         system.CurrentProcess()->GetHandleTable().Get<Kernel::TransferMemory>(handle);
 
 
     if (transfer_mem == nullptr) {
     if (transfer_mem == nullptr) {
-        LOG_ERROR(Service_AM, "shared_mem is a nullpr for handle={:08X}", handle);
+        LOG_ERROR(Service_AM, "transfer_mem is a nullptr for handle={:08X}", handle);
         IPC::ResponseBuilder rb{ctx, 2};
         IPC::ResponseBuilder rb{ctx, 2};
         rb.Push(RESULT_UNKNOWN);
         rb.Push(RESULT_UNKNOWN);
         return;
         return;

+ 1 - 0
src/core/hle/service/am/am.h

@@ -254,6 +254,7 @@ private:
     void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
     void CreateLibraryApplet(Kernel::HLERequestContext& ctx);
     void CreateStorage(Kernel::HLERequestContext& ctx);
     void CreateStorage(Kernel::HLERequestContext& ctx);
     void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
     void CreateTransferMemoryStorage(Kernel::HLERequestContext& ctx);
+    void CreateHandleStorage(Kernel::HLERequestContext& ctx);
 };
 };
 
 
 class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {
 class IApplicationFunctions final : public ServiceFramework<IApplicationFunctions> {

+ 9 - 9
src/core/hle/service/am/applets/applets.cpp

@@ -241,31 +241,31 @@ void AppletManager::ClearAll() {
     frontend = {};
     frontend = {};
 }
 }
 
 
-std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
+std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id, LibraryAppletMode mode) const {
     switch (id) {
     switch (id) {
     case AppletId::Auth:
     case AppletId::Auth:
-        return std::make_shared<Auth>(system, *frontend.parental_controls);
+        return std::make_shared<Auth>(system, mode, *frontend.parental_controls);
     case AppletId::Controller:
     case AppletId::Controller:
-        return std::make_shared<Controller>(system, *frontend.controller);
+        return std::make_shared<Controller>(system, mode, *frontend.controller);
     case AppletId::Error:
     case AppletId::Error:
-        return std::make_shared<Error>(system, *frontend.error);
+        return std::make_shared<Error>(system, mode, *frontend.error);
     case AppletId::ProfileSelect:
     case AppletId::ProfileSelect:
-        return std::make_shared<ProfileSelect>(system, *frontend.profile_select);
+        return std::make_shared<ProfileSelect>(system, mode, *frontend.profile_select);
     case AppletId::SoftwareKeyboard:
     case AppletId::SoftwareKeyboard:
-        return std::make_shared<SoftwareKeyboard>(system, *frontend.software_keyboard);
+        return std::make_shared<SoftwareKeyboard>(system, mode, *frontend.software_keyboard);
     case AppletId::Web:
     case AppletId::Web:
     case AppletId::Shop:
     case AppletId::Shop:
     case AppletId::OfflineWeb:
     case AppletId::OfflineWeb:
     case AppletId::LoginShare:
     case AppletId::LoginShare:
     case AppletId::WebAuth:
     case AppletId::WebAuth:
-        return std::make_shared<WebBrowser>(system, *frontend.web_browser);
+        return std::make_shared<WebBrowser>(system, mode, *frontend.web_browser);
     case AppletId::PhotoViewer:
     case AppletId::PhotoViewer:
-        return std::make_shared<PhotoViewer>(system, *frontend.photo_viewer);
+        return std::make_shared<PhotoViewer>(system, mode, *frontend.photo_viewer);
     default:
     default:
         UNIMPLEMENTED_MSG(
         UNIMPLEMENTED_MSG(
             "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
             "No backend implementation exists for applet_id={:02X}! Falling back to stub applet.",
             static_cast<u8>(id));
             static_cast<u8>(id));
-        return std::make_shared<StubApplet>(system, id);
+        return std::make_shared<StubApplet>(system, id, mode);
     }
     }
 }
 }
 
 

+ 9 - 1
src/core/hle/service/am/applets/applets.h

@@ -62,6 +62,14 @@ enum class AppletId : u32 {
     MyPage = 0x1A,
     MyPage = 0x1A,
 };
 };
 
 
+enum class LibraryAppletMode : u32 {
+    AllForeground = 0,
+    Background = 1,
+    NoUI = 2,
+    BackgroundIndirectDisplay = 3,
+    AllForegroundInitiallyHidden = 4,
+};
+
 class AppletDataBroker final {
 class AppletDataBroker final {
 public:
 public:
     explicit AppletDataBroker(Kernel::KernelCore& kernel_);
     explicit AppletDataBroker(Kernel::KernelCore& kernel_);
@@ -200,7 +208,7 @@ public:
     void SetDefaultAppletsIfMissing();
     void SetDefaultAppletsIfMissing();
     void ClearAll();
     void ClearAll();
 
 
-    std::shared_ptr<Applet> GetApplet(AppletId id) const;
+    std::shared_ptr<Applet> GetApplet(AppletId id, LibraryAppletMode mode) const;
 
 
 private:
 private:
     AppletFrontendSet frontend;
     AppletFrontendSet frontend;

+ 3 - 2
src/core/hle/service/am/applets/controller.cpp

@@ -45,8 +45,9 @@ static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
     };
     };
 }
 }
 
 
-Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
+Controller::Controller(Core::System& system_, LibraryAppletMode applet_mode_,
+                       const Core::Frontend::ControllerApplet& frontend_)
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {}
 
 
 Controller::~Controller() = default;
 Controller::~Controller() = default;
 
 

+ 3 - 1
src/core/hle/service/am/applets/controller.h

@@ -106,7 +106,8 @@ static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
 
 
 class Controller final : public Applet {
 class Controller final : public Applet {
 public:
 public:
-    explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
+    explicit Controller(Core::System& system_, LibraryAppletMode applet_mode_,
+                        const Core::Frontend::ControllerApplet& frontend_);
     ~Controller() override;
     ~Controller() override;
 
 
     void Initialize() override;
     void Initialize() override;
@@ -119,6 +120,7 @@ public:
     void ConfigurationComplete();
     void ConfigurationComplete();
 
 
 private:
 private:
+    LibraryAppletMode applet_mode;
     const Core::Frontend::ControllerApplet& frontend;
     const Core::Frontend::ControllerApplet& frontend;
     Core::System& system;
     Core::System& system;
 
 

+ 3 - 2
src/core/hle/service/am/applets/error.cpp

@@ -86,8 +86,9 @@ ResultCode Decode64BitError(u64 error) {
 
 
 } // Anonymous namespace
 } // Anonymous namespace
 
 
-Error::Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
+Error::Error(Core::System& system_, LibraryAppletMode applet_mode_,
+             const Core::Frontend::ErrorApplet& frontend_)
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {}
 
 
 Error::~Error() = default;
 Error::~Error() = default;
 
 

+ 3 - 1
src/core/hle/service/am/applets/error.h

@@ -25,7 +25,8 @@ enum class ErrorAppletMode : u8 {
 
 
 class Error final : public Applet {
 class Error final : public Applet {
 public:
 public:
-    explicit Error(Core::System& system_, const Core::Frontend::ErrorApplet& frontend_);
+    explicit Error(Core::System& system_, LibraryAppletMode applet_mode_,
+                   const Core::Frontend::ErrorApplet& frontend_);
     ~Error() override;
     ~Error() override;
 
 
     void Initialize() override;
     void Initialize() override;
@@ -40,6 +41,7 @@ public:
 private:
 private:
     union ErrorArguments;
     union ErrorArguments;
 
 
+    LibraryAppletMode applet_mode;
     const Core::Frontend::ErrorApplet& frontend;
     const Core::Frontend::ErrorApplet& frontend;
     ResultCode error_code = RESULT_SUCCESS;
     ResultCode error_code = RESULT_SUCCESS;
     ErrorAppletMode mode = ErrorAppletMode::ShowError;
     ErrorAppletMode mode = ErrorAppletMode::ShowError;

+ 8 - 6
src/core/hle/service/am/applets/general_backend.cpp

@@ -37,8 +37,9 @@ static void LogCurrentStorage(AppletDataBroker& broker, std::string_view prefix)
     }
     }
 }
 }
 
 
-Auth::Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
+Auth::Auth(Core::System& system_, LibraryAppletMode applet_mode_,
+           Core::Frontend::ParentalControlsApplet& frontend_)
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {}
 
 
 Auth::~Auth() = default;
 Auth::~Auth() = default;
 
 
@@ -152,8 +153,9 @@ void Auth::AuthFinished(bool is_successful) {
     broker.SignalStateChanged();
     broker.SignalStateChanged();
 }
 }
 
 
-PhotoViewer::PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
+PhotoViewer::PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_,
+                         const Core::Frontend::PhotoViewerApplet& frontend_)
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {}
 
 
 PhotoViewer::~PhotoViewer() = default;
 PhotoViewer::~PhotoViewer() = default;
 
 
@@ -202,8 +204,8 @@ void PhotoViewer::ViewFinished() {
     broker.SignalStateChanged();
     broker.SignalStateChanged();
 }
 }
 
 
-StubApplet::StubApplet(Core::System& system_, AppletId id_)
-    : Applet{system_.Kernel()}, id{id_}, system{system_} {}
+StubApplet::StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_)
+    : Applet{system_.Kernel()}, id{id_}, applet_mode{applet_mode_}, system{system_} {}
 
 
 StubApplet::~StubApplet() = default;
 StubApplet::~StubApplet() = default;
 
 

+ 8 - 3
src/core/hle/service/am/applets/general_backend.h

@@ -20,7 +20,8 @@ enum class AuthAppletType : u32 {
 
 
 class Auth final : public Applet {
 class Auth final : public Applet {
 public:
 public:
-    explicit Auth(Core::System& system_, Core::Frontend::ParentalControlsApplet& frontend_);
+    explicit Auth(Core::System& system_, LibraryAppletMode applet_mode_,
+                  Core::Frontend::ParentalControlsApplet& frontend_);
     ~Auth() override;
     ~Auth() override;
 
 
     void Initialize() override;
     void Initialize() override;
@@ -32,6 +33,7 @@ public:
     void AuthFinished(bool is_successful = true);
     void AuthFinished(bool is_successful = true);
 
 
 private:
 private:
+    LibraryAppletMode applet_mode;
     Core::Frontend::ParentalControlsApplet& frontend;
     Core::Frontend::ParentalControlsApplet& frontend;
     Core::System& system;
     Core::System& system;
     bool complete = false;
     bool complete = false;
@@ -50,7 +52,8 @@ enum class PhotoViewerAppletMode : u8 {
 
 
 class PhotoViewer final : public Applet {
 class PhotoViewer final : public Applet {
 public:
 public:
-    explicit PhotoViewer(Core::System& system_, const Core::Frontend::PhotoViewerApplet& frontend_);
+    explicit PhotoViewer(Core::System& system_, LibraryAppletMode applet_mode_,
+                         const Core::Frontend::PhotoViewerApplet& frontend_);
     ~PhotoViewer() override;
     ~PhotoViewer() override;
 
 
     void Initialize() override;
     void Initialize() override;
@@ -62,6 +65,7 @@ public:
     void ViewFinished();
     void ViewFinished();
 
 
 private:
 private:
+    LibraryAppletMode applet_mode;
     const Core::Frontend::PhotoViewerApplet& frontend;
     const Core::Frontend::PhotoViewerApplet& frontend;
     bool complete = false;
     bool complete = false;
     PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
     PhotoViewerAppletMode mode = PhotoViewerAppletMode::CurrentApp;
@@ -70,7 +74,7 @@ private:
 
 
 class StubApplet final : public Applet {
 class StubApplet final : public Applet {
 public:
 public:
-    explicit StubApplet(Core::System& system_, AppletId id_);
+    explicit StubApplet(Core::System& system_, AppletId id_, LibraryAppletMode applet_mode_);
     ~StubApplet() override;
     ~StubApplet() override;
 
 
     void Initialize() override;
     void Initialize() override;
@@ -82,6 +86,7 @@ public:
 
 
 private:
 private:
     AppletId id;
     AppletId id;
+    LibraryAppletMode applet_mode;
     Core::System& system;
     Core::System& system;
 };
 };
 
 

+ 2 - 2
src/core/hle/service/am/applets/profile_select.cpp

@@ -15,9 +15,9 @@ namespace Service::AM::Applets {
 
 
 constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
 constexpr ResultCode ERR_USER_CANCELLED_SELECTION{ErrorModule::Account, 1};
 
 
-ProfileSelect::ProfileSelect(Core::System& system_,
+ProfileSelect::ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_,
                              const Core::Frontend::ProfileSelectApplet& frontend_)
                              const Core::Frontend::ProfileSelectApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {}
 
 
 ProfileSelect::~ProfileSelect() = default;
 ProfileSelect::~ProfileSelect() = default;
 
 

+ 2 - 1
src/core/hle/service/am/applets/profile_select.h

@@ -33,7 +33,7 @@ static_assert(sizeof(UserSelectionOutput) == 0x18, "UserSelectionOutput has inco
 
 
 class ProfileSelect final : public Applet {
 class ProfileSelect final : public Applet {
 public:
 public:
-    explicit ProfileSelect(Core::System& system_,
+    explicit ProfileSelect(Core::System& system_, LibraryAppletMode applet_mode_,
                            const Core::Frontend::ProfileSelectApplet& frontend_);
                            const Core::Frontend::ProfileSelectApplet& frontend_);
     ~ProfileSelect() override;
     ~ProfileSelect() override;
 
 
@@ -47,6 +47,7 @@ public:
     void SelectionComplete(std::optional<Common::UUID> uuid);
     void SelectionComplete(std::optional<Common::UUID> uuid);
 
 
 private:
 private:
+    LibraryAppletMode applet_mode;
     const Core::Frontend::ProfileSelectApplet& frontend;
     const Core::Frontend::ProfileSelectApplet& frontend;
 
 
     UserSelectionConfig config;
     UserSelectionConfig config;

+ 1015 - 138
src/core/hle/service/am/applets/software_keyboard.cpp

@@ -1,93 +1,80 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2021 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
-#include <cstring>
-#include "common/assert.h"
 #include "common/string_util.h"
 #include "common/string_util.h"
 #include "core/core.h"
 #include "core/core.h"
 #include "core/frontend/applets/software_keyboard.h"
 #include "core/frontend/applets/software_keyboard.h"
-#include "core/hle/result.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/am.h"
 #include "core/hle/service/am/applets/software_keyboard.h"
 #include "core/hle/service/am/applets/software_keyboard.h"
 
 
 namespace Service::AM::Applets {
 namespace Service::AM::Applets {
 
 
 namespace {
 namespace {
-enum class Request : u32 {
-    Finalize = 0x4,
-    SetUserWordInfo = 0x6,
-    SetCustomizeDic = 0x7,
-    Calc = 0xa,
-    SetCustomizedDictionaries = 0xb,
-    UnsetCustomizedDictionaries = 0xc,
-    UnknownD = 0xd,
-    UnknownE = 0xe,
-};
-constexpr std::size_t SWKBD_INLINE_INIT_SIZE = 0x8;
-constexpr std::size_t SWKBD_OUTPUT_BUFFER_SIZE = 0x7D8;
-constexpr std::size_t SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE = 0x7D4;
-constexpr std::size_t DEFAULT_MAX_LENGTH = 500;
-constexpr bool INTERACTIVE_STATUS_OK = false;
+
+// The maximum number of UTF-16 characters that can be input into the swkbd text field.
+constexpr u32 DEFAULT_MAX_TEXT_LENGTH = 500;
+
+constexpr std::size_t REPLY_BASE_SIZE = sizeof(SwkbdState) + sizeof(SwkbdReplyType);
+constexpr std::size_t REPLY_UTF8_SIZE = 0x7D4;
+constexpr std::size_t REPLY_UTF16_SIZE = 0x3EC;
+
+constexpr const char* GetTextCheckResultName(SwkbdTextCheckResult text_check_result) {
+    switch (text_check_result) {
+    case SwkbdTextCheckResult::Success:
+        return "Success";
+    case SwkbdTextCheckResult::Failure:
+        return "Failure";
+    case SwkbdTextCheckResult::Confirm:
+        return "Confirm";
+    case SwkbdTextCheckResult::Silent:
+        return "Silent";
+    default:
+        UNIMPLEMENTED_MSG("Unknown TextCheckResult={}", text_check_result);
+        return "Unknown";
+    }
+}
+
+void SetReplyBase(std::vector<u8>& reply, SwkbdState state, SwkbdReplyType reply_type) {
+    std::memcpy(reply.data(), &state, sizeof(SwkbdState));
+    std::memcpy(reply.data() + sizeof(SwkbdState), &reply_type, sizeof(SwkbdReplyType));
+}
+
 } // Anonymous namespace
 } // Anonymous namespace
-static Core::Frontend::SoftwareKeyboardParameters ConvertToFrontendParameters(
-    KeyboardConfig config, std::u16string initial_text) {
-    Core::Frontend::SoftwareKeyboardParameters params{};
-
-    params.submit_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
-        config.submit_text.data(), config.submit_text.size());
-    params.header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
-        config.header_text.data(), config.header_text.size());
-    params.sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.sub_text.data(),
-                                                                       config.sub_text.size());
-    params.guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(config.guide_text.data(),
-                                                                         config.guide_text.size());
-    params.initial_text = std::move(initial_text);
-    params.max_length = config.length_limit == 0 ? DEFAULT_MAX_LENGTH : config.length_limit;
-    params.password = static_cast<bool>(config.is_password);
-    params.cursor_at_beginning = static_cast<bool>(config.initial_cursor_position);
-    params.value = static_cast<u8>(config.keyset_disable_bitmask);
-
-    return params;
-}
-
-SoftwareKeyboard::SoftwareKeyboard(Core::System& system_,
-                                   const Core::Frontend::SoftwareKeyboardApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend{frontend_}, system{system_} {}
+
+SoftwareKeyboard::SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_,
+                                   Core::Frontend::SoftwareKeyboardApplet& frontend_)
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend{frontend_}, system{system_} {}
 
 
 SoftwareKeyboard::~SoftwareKeyboard() = default;
 SoftwareKeyboard::~SoftwareKeyboard() = default;
 
 
 void SoftwareKeyboard::Initialize() {
 void SoftwareKeyboard::Initialize() {
-    complete = false;
-    is_inline = false;
-    initial_text.clear();
-    final_data.clear();
-
     Applet::Initialize();
     Applet::Initialize();
 
 
-    const auto keyboard_config_storage = broker.PopNormalDataToApplet();
-    ASSERT(keyboard_config_storage != nullptr);
-    const auto& keyboard_config = keyboard_config_storage->GetData();
-
-    if (keyboard_config.size() == SWKBD_INLINE_INIT_SIZE) {
-        is_inline = true;
-        return;
-    }
-
-    ASSERT(keyboard_config.size() >= sizeof(KeyboardConfig));
-    std::memcpy(&config, keyboard_config.data(), sizeof(KeyboardConfig));
+    LOG_INFO(Service_AM, "Initializing Software Keyboard Applet with LibraryAppletMode={}",
+             applet_mode);
 
 
-    const auto work_buffer_storage = broker.PopNormalDataToApplet();
-    ASSERT_OR_EXECUTE(work_buffer_storage != nullptr, { return; });
-    const auto& work_buffer = work_buffer_storage->GetData();
+    LOG_DEBUG(Service_AM,
+              "Initializing Applet with common_args: arg_version={}, lib_version={}, "
+              "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
+              common_args.arguments_version, common_args.library_version,
+              common_args.play_startup_sound, common_args.size, common_args.system_tick,
+              common_args.theme_color);
 
 
-    if (config.initial_string_size == 0)
-        return;
+    swkbd_applet_version = SwkbdAppletVersion{common_args.library_version};
 
 
-    std::vector<char16_t> string(config.initial_string_size);
-    std::memcpy(string.data(), work_buffer.data() + config.initial_string_offset,
-                string.size() * 2);
-    initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size());
+    switch (applet_mode) {
+    case LibraryAppletMode::AllForeground:
+        InitializeForeground();
+        break;
+    case LibraryAppletMode::Background:
+    case LibraryAppletMode::BackgroundIndirectDisplay:
+        InitializeBackground(applet_mode);
+        break;
+    default:
+        UNREACHABLE_MSG("Invalid LibraryAppletMode={}", applet_mode);
+        break;
+    }
 }
 }
 
 
 bool SoftwareKeyboard::TransactionComplete() const {
 bool SoftwareKeyboard::TransactionComplete() const {
@@ -95,106 +82,996 @@ bool SoftwareKeyboard::TransactionComplete() const {
 }
 }
 
 
 ResultCode SoftwareKeyboard::GetStatus() const {
 ResultCode SoftwareKeyboard::GetStatus() const {
-    return RESULT_SUCCESS;
+    return status;
 }
 }
 
 
 void SoftwareKeyboard::ExecuteInteractive() {
 void SoftwareKeyboard::ExecuteInteractive() {
-    if (complete)
+    if (complete) {
         return;
         return;
+    }
 
 
-    const auto storage = broker.PopInteractiveDataToApplet();
-    ASSERT(storage != nullptr);
-    const auto data = storage->GetData();
-    if (!is_inline) {
-        const auto status = static_cast<bool>(data[0]);
-        if (status == INTERACTIVE_STATUS_OK) {
-            complete = true;
-        } else {
-            std::array<char16_t, SWKBD_OUTPUT_INTERACTIVE_BUFFER_SIZE / 2 - 2> string;
-            std::memcpy(string.data(), data.data() + 4, string.size() * 2);
-            frontend.SendTextCheckDialog(
-                Common::UTF16StringFromFixedZeroTerminatedBuffer(string.data(), string.size()),
-                [this] { broker.SignalStateChanged(); });
-        }
+    if (is_background) {
+        ProcessInlineKeyboardRequest();
     } else {
     } else {
-        Request request{};
-        std::memcpy(&request, data.data(), sizeof(Request));
+        ProcessTextCheck();
+    }
+}
+
+void SoftwareKeyboard::Execute() {
+    if (complete) {
+        return;
+    }
+
+    if (is_background) {
+        return;
+    }
+
+    ShowNormalKeyboard();
+}
 
 
-        switch (request) {
-        case Request::Finalize:
-            complete = true;
-            broker.SignalStateChanged();
+void SoftwareKeyboard::SubmitTextNormal(SwkbdResult result, std::u16string submitted_text) {
+    if (complete) {
+        return;
+    }
+
+    if (swkbd_config_common.use_text_check && result == SwkbdResult::Ok) {
+        SubmitForTextCheck(submitted_text);
+    } else {
+        SubmitNormalOutputAndExit(result, submitted_text);
+    }
+}
+
+void SoftwareKeyboard::SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text,
+                                        s32 cursor_position) {
+    if (complete) {
+        return;
+    }
+
+    current_text = std::move(submitted_text);
+    current_cursor_position = cursor_position;
+
+    if (inline_use_utf8) {
+        switch (reply_type) {
+        case SwkbdReplyType::ChangedString:
+            reply_type = SwkbdReplyType::ChangedStringUtf8;
             break;
             break;
-        case Request::Calc: {
-            broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::vector<u8>{1}));
-            broker.SignalStateChanged();
+        case SwkbdReplyType::MovedCursor:
+            reply_type = SwkbdReplyType::MovedCursorUtf8;
+            break;
+        case SwkbdReplyType::DecidedEnter:
+            reply_type = SwkbdReplyType::DecidedEnterUtf8;
+            break;
+        default:
             break;
             break;
         }
         }
+    }
+
+    if (use_changed_string_v2) {
+        switch (reply_type) {
+        case SwkbdReplyType::ChangedString:
+            reply_type = SwkbdReplyType::ChangedStringV2;
+            break;
+        case SwkbdReplyType::ChangedStringUtf8:
+            reply_type = SwkbdReplyType::ChangedStringUtf8V2;
+            break;
         default:
         default:
-            UNIMPLEMENTED_MSG("Request {:X} is not implemented", request);
             break;
             break;
         }
         }
     }
     }
+
+    if (use_moved_cursor_v2) {
+        switch (reply_type) {
+        case SwkbdReplyType::MovedCursor:
+            reply_type = SwkbdReplyType::MovedCursorV2;
+            break;
+        case SwkbdReplyType::MovedCursorUtf8:
+            reply_type = SwkbdReplyType::MovedCursorUtf8V2;
+            break;
+        default:
+            break;
+        }
+    }
+
+    SendReply(reply_type);
 }
 }
 
 
-void SoftwareKeyboard::Execute() {
-    if (complete) {
-        broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(final_data)));
-        broker.SignalStateChanged();
+void SoftwareKeyboard::InitializeForeground() {
+    LOG_INFO(Service_AM, "Initializing Normal Software Keyboard Applet.");
+
+    is_background = false;
+
+    const auto swkbd_config_storage = broker.PopNormalDataToApplet();
+    ASSERT(swkbd_config_storage != nullptr);
+
+    const auto& swkbd_config_data = swkbd_config_storage->GetData();
+    ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon));
+
+    std::memcpy(&swkbd_config_common, swkbd_config_data.data(), sizeof(SwkbdConfigCommon));
+
+    switch (swkbd_applet_version) {
+    case SwkbdAppletVersion::Version5:
+    case SwkbdAppletVersion::Version65542:
+        ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld));
+        std::memcpy(&swkbd_config_old, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
+                    sizeof(SwkbdConfigOld));
+        break;
+    case SwkbdAppletVersion::Version196615:
+    case SwkbdAppletVersion::Version262152:
+    case SwkbdAppletVersion::Version327689:
+        ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigOld2));
+        std::memcpy(&swkbd_config_old2, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
+                    sizeof(SwkbdConfigOld2));
+        break;
+    case SwkbdAppletVersion::Version393227:
+    case SwkbdAppletVersion::Version524301:
+        ASSERT(swkbd_config_data.size() == sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew));
+        std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
+                    sizeof(SwkbdConfigNew));
+        break;
+    default:
+        UNIMPLEMENTED_MSG("Unknown SwkbdConfig revision={} with size={}", swkbd_applet_version,
+                          swkbd_config_data.size());
+        ASSERT(swkbd_config_data.size() >= sizeof(SwkbdConfigCommon) + sizeof(SwkbdConfigNew));
+        std::memcpy(&swkbd_config_new, swkbd_config_data.data() + sizeof(SwkbdConfigCommon),
+                    sizeof(SwkbdConfigNew));
+        break;
+    }
+
+    const auto work_buffer_storage = broker.PopNormalDataToApplet();
+    ASSERT(work_buffer_storage != nullptr);
+
+    if (swkbd_config_common.initial_string_length == 0) {
+        InitializeFrontendKeyboard();
         return;
         return;
     }
     }
 
 
-    const auto parameters = ConvertToFrontendParameters(config, initial_text);
-    if (!is_inline) {
-        frontend.RequestText(
-            [this](std::optional<std::u16string> text) { WriteText(std::move(text)); }, parameters);
+    const auto& work_buffer = work_buffer_storage->GetData();
+
+    std::vector<char16_t> initial_string(swkbd_config_common.initial_string_length);
+
+    std::memcpy(initial_string.data(),
+                work_buffer.data() + swkbd_config_common.initial_string_offset,
+                swkbd_config_common.initial_string_length * sizeof(char16_t));
+
+    initial_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(initial_string.data(),
+                                                                    initial_string.size());
+
+    LOG_DEBUG(Service_AM, "\nInitial Text: {}", Common::UTF16ToUTF8(initial_text));
+
+    InitializeFrontendKeyboard();
+}
+
+void SoftwareKeyboard::InitializeBackground(LibraryAppletMode applet_mode) {
+    LOG_INFO(Service_AM, "Initializing Inline Software Keyboard Applet.");
+
+    is_background = true;
+
+    const auto swkbd_inline_initialize_arg_storage = broker.PopNormalDataToApplet();
+    ASSERT(swkbd_inline_initialize_arg_storage != nullptr);
+
+    const auto& swkbd_inline_initialize_arg = swkbd_inline_initialize_arg_storage->GetData();
+    ASSERT(swkbd_inline_initialize_arg.size() == sizeof(SwkbdInitializeArg));
+
+    std::memcpy(&swkbd_initialize_arg, swkbd_inline_initialize_arg.data(),
+                swkbd_inline_initialize_arg.size());
+
+    if (swkbd_initialize_arg.library_applet_mode_flag) {
+        ASSERT(applet_mode == LibraryAppletMode::Background);
+    } else {
+        ASSERT(applet_mode == LibraryAppletMode::BackgroundIndirectDisplay);
+    }
+}
+
+void SoftwareKeyboard::ProcessTextCheck() {
+    const auto text_check_storage = broker.PopInteractiveDataToApplet();
+    ASSERT(text_check_storage != nullptr);
+
+    const auto& text_check_data = text_check_storage->GetData();
+    ASSERT(text_check_data.size() == sizeof(SwkbdTextCheck));
+
+    SwkbdTextCheck swkbd_text_check;
+
+    std::memcpy(&swkbd_text_check, text_check_data.data(), sizeof(SwkbdTextCheck));
+
+    std::u16string text_check_message = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+        swkbd_text_check.text_check_message.data(), swkbd_text_check.text_check_message.size());
+
+    LOG_INFO(Service_AM, "\nTextCheckResult: {}\nTextCheckMessage: {}",
+             GetTextCheckResultName(swkbd_text_check.text_check_result),
+             Common::UTF16ToUTF8(text_check_message));
+
+    switch (swkbd_text_check.text_check_result) {
+    case SwkbdTextCheckResult::Success:
+        SubmitNormalOutputAndExit(SwkbdResult::Ok, current_text);
+        break;
+    case SwkbdTextCheckResult::Failure:
+        ShowTextCheckDialog(SwkbdTextCheckResult::Failure, text_check_message);
+        break;
+    case SwkbdTextCheckResult::Confirm:
+        ShowTextCheckDialog(SwkbdTextCheckResult::Confirm, text_check_message);
+        break;
+    case SwkbdTextCheckResult::Silent:
+    default:
+        break;
     }
     }
 }
 }
 
 
-void SoftwareKeyboard::WriteText(std::optional<std::u16string> text) {
-    std::vector<u8> output_main(SWKBD_OUTPUT_BUFFER_SIZE);
+void SoftwareKeyboard::ProcessInlineKeyboardRequest() {
+    const auto request_data_storage = broker.PopInteractiveDataToApplet();
+    ASSERT(request_data_storage != nullptr);
+
+    const auto& request_data = request_data_storage->GetData();
+    ASSERT(request_data.size() >= sizeof(SwkbdRequestCommand));
 
 
-    if (text.has_value()) {
-        std::vector<u8> output_sub(SWKBD_OUTPUT_BUFFER_SIZE);
+    SwkbdRequestCommand request_command;
 
 
-        if (config.utf_8) {
-            const u64 size = text->size() + sizeof(u64);
-            const auto new_text = Common::UTF16ToUTF8(*text);
+    std::memcpy(&request_command, request_data.data(), sizeof(SwkbdRequestCommand));
 
 
-            std::memcpy(output_sub.data(), &size, sizeof(u64));
-            std::memcpy(output_sub.data() + 8, new_text.data(),
-                        std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 8));
+    switch (request_command) {
+    case SwkbdRequestCommand::Finalize:
+        RequestFinalize(request_data);
+        break;
+    case SwkbdRequestCommand::SetUserWordInfo:
+        RequestSetUserWordInfo(request_data);
+        break;
+    case SwkbdRequestCommand::SetCustomizeDic:
+        RequestSetCustomizeDic(request_data);
+        break;
+    case SwkbdRequestCommand::Calc:
+        RequestCalc(request_data);
+        break;
+    case SwkbdRequestCommand::SetCustomizedDictionaries:
+        RequestSetCustomizedDictionaries(request_data);
+        break;
+    case SwkbdRequestCommand::UnsetCustomizedDictionaries:
+        RequestUnsetCustomizedDictionaries(request_data);
+        break;
+    case SwkbdRequestCommand::SetChangedStringV2Flag:
+        RequestSetChangedStringV2Flag(request_data);
+        break;
+    case SwkbdRequestCommand::SetMovedCursorV2Flag:
+        RequestSetMovedCursorV2Flag(request_data);
+        break;
+    default:
+        UNIMPLEMENTED_MSG("Unknown SwkbdRequestCommand={}", request_command);
+        break;
+    }
+}
 
 
-            output_main[0] = INTERACTIVE_STATUS_OK;
-            std::memcpy(output_main.data() + 4, new_text.data(),
-                        std::min(new_text.size(), SWKBD_OUTPUT_BUFFER_SIZE - 4));
-        } else {
-            const u64 size = text->size() * 2 + sizeof(u64);
-            std::memcpy(output_sub.data(), &size, sizeof(u64));
-            std::memcpy(output_sub.data() + 8, text->data(),
-                        std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 8));
+void SoftwareKeyboard::SubmitNormalOutputAndExit(SwkbdResult result,
+                                                 std::u16string submitted_text) {
+    std::vector<u8> out_data(sizeof(SwkbdResult) + STRING_BUFFER_SIZE);
 
 
-            output_main[0] = INTERACTIVE_STATUS_OK;
-            std::memcpy(output_main.data() + 4, text->data(),
-                        std::min(text->size() * 2, SWKBD_OUTPUT_BUFFER_SIZE - 4));
-        }
+    if (swkbd_config_common.use_utf8) {
+        std::string utf8_submitted_text = Common::UTF16ToUTF8(submitted_text);
 
 
-        complete = !config.text_check;
-        final_data = output_main;
+        LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-8 Submitted Text: {}", result,
+                  utf8_submitted_text);
 
 
-        if (complete) {
-            broker.PushNormalDataFromApplet(
-                std::make_shared<IStorage>(system, std::move(output_main)));
-            broker.SignalStateChanged();
-        } else {
-            broker.PushInteractiveDataFromApplet(
-                std::make_shared<IStorage>(system, std::move(output_sub)));
-        }
+        std::memcpy(out_data.data(), &result, sizeof(SwkbdResult));
+        std::memcpy(out_data.data() + sizeof(SwkbdResult), utf8_submitted_text.data(),
+                    utf8_submitted_text.size());
     } else {
     } else {
-        output_main[0] = 1;
-        complete = true;
-        broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(output_main)));
-        broker.SignalStateChanged();
+        LOG_DEBUG(Service_AM, "\nSwkbdResult: {}\nUTF-16 Submitted Text: {}", result,
+                  Common::UTF16ToUTF8(submitted_text));
+
+        std::memcpy(out_data.data(), &result, sizeof(SwkbdResult));
+        std::memcpy(out_data.data() + sizeof(SwkbdResult), submitted_text.data(),
+                    submitted_text.size() * sizeof(char16_t));
+    }
+
+    broker.PushNormalDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
+
+    ExitKeyboard();
+}
+
+void SoftwareKeyboard::SubmitForTextCheck(std::u16string submitted_text) {
+    current_text = std::move(submitted_text);
+
+    std::vector<u8> out_data(sizeof(u64) + STRING_BUFFER_SIZE);
+
+    if (swkbd_config_common.use_utf8) {
+        std::string utf8_submitted_text = Common::UTF16ToUTF8(current_text);
+        const u64 buffer_size = sizeof(u64) + utf8_submitted_text.size();
+
+        LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-8 Submitted Text: {}", buffer_size,
+                  utf8_submitted_text);
+
+        std::memcpy(out_data.data(), &buffer_size, sizeof(u64));
+        std::memcpy(out_data.data() + sizeof(u64), utf8_submitted_text.data(),
+                    utf8_submitted_text.size());
+    } else {
+        const u64 buffer_size = sizeof(u64) + current_text.size() * sizeof(char16_t);
+
+        LOG_DEBUG(Service_AM, "\nBuffer Size: {}\nUTF-16 Submitted Text: {}", buffer_size,
+                  Common::UTF16ToUTF8(current_text));
+
+        std::memcpy(out_data.data(), &buffer_size, sizeof(u64));
+        std::memcpy(out_data.data() + sizeof(u64), current_text.data(),
+                    current_text.size() * sizeof(char16_t));
+    }
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(out_data)));
+}
+
+void SoftwareKeyboard::SendReply(SwkbdReplyType reply_type) {
+    switch (reply_type) {
+    case SwkbdReplyType::FinishedInitialize:
+        ReplyFinishedInitialize();
+        break;
+    case SwkbdReplyType::Default:
+        ReplyDefault();
+        break;
+    case SwkbdReplyType::ChangedString:
+        ReplyChangedString();
+        break;
+    case SwkbdReplyType::MovedCursor:
+        ReplyMovedCursor();
+        break;
+    case SwkbdReplyType::MovedTab:
+        ReplyMovedTab();
+        break;
+    case SwkbdReplyType::DecidedEnter:
+        ReplyDecidedEnter();
+        break;
+    case SwkbdReplyType::DecidedCancel:
+        ReplyDecidedCancel();
+        break;
+    case SwkbdReplyType::ChangedStringUtf8:
+        ReplyChangedStringUtf8();
+        break;
+    case SwkbdReplyType::MovedCursorUtf8:
+        ReplyMovedCursorUtf8();
+        break;
+    case SwkbdReplyType::DecidedEnterUtf8:
+        ReplyDecidedEnterUtf8();
+        break;
+    case SwkbdReplyType::UnsetCustomizeDic:
+        ReplyUnsetCustomizeDic();
+        break;
+    case SwkbdReplyType::ReleasedUserWordInfo:
+        ReplyReleasedUserWordInfo();
+        break;
+    case SwkbdReplyType::UnsetCustomizedDictionaries:
+        ReplyUnsetCustomizedDictionaries();
+        break;
+    case SwkbdReplyType::ChangedStringV2:
+        ReplyChangedStringV2();
+        break;
+    case SwkbdReplyType::MovedCursorV2:
+        ReplyMovedCursorV2();
+        break;
+    case SwkbdReplyType::ChangedStringUtf8V2:
+        ReplyChangedStringUtf8V2();
+        break;
+    case SwkbdReplyType::MovedCursorUtf8V2:
+        ReplyMovedCursorUtf8V2();
+        break;
+    default:
+        UNIMPLEMENTED_MSG("Unknown SwkbdReplyType={}", reply_type);
+        ReplyDefault();
+        break;
+    }
+}
+
+void SoftwareKeyboard::ChangeState(SwkbdState state) {
+    swkbd_state = state;
+
+    ReplyDefault();
+}
+
+void SoftwareKeyboard::InitializeFrontendKeyboard() {
+    if (is_background) {
+        const auto& appear_arg = swkbd_calc_arg.appear_arg;
+
+        std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+            appear_arg.ok_text.data(), appear_arg.ok_text.size());
+
+        const u32 max_text_length =
+            appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+                ? appear_arg.max_text_length
+                : DEFAULT_MAX_TEXT_LENGTH;
+
+        const u32 min_text_length =
+            appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+        const s32 initial_cursor_position =
+            current_cursor_position > 0 ? current_cursor_position : 0;
+
+        const auto text_draw_type =
+            max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+
+        Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+            .ok_text{ok_text},
+            .header_text{},
+            .sub_text{},
+            .guide_text{},
+            .initial_text{current_text},
+            .max_text_length{max_text_length},
+            .min_text_length{min_text_length},
+            .initial_cursor_position{initial_cursor_position},
+            .type{appear_arg.type},
+            .password_mode{SwkbdPasswordMode::Disabled},
+            .text_draw_type{text_draw_type},
+            .key_disable_flags{appear_arg.key_disable_flags},
+            .use_blur_background{false},
+            .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
+            .enable_return_button{appear_arg.enable_return_button},
+            .disable_cancel_button{appear_arg.disable_cancel_button},
+        };
+
+        frontend.InitializeKeyboard(
+            true, std::move(initialize_parameters), {},
+            [this](SwkbdReplyType reply_type, std::u16string submitted_text, s32 cursor_position) {
+                SubmitTextInline(reply_type, submitted_text, cursor_position);
+            });
+    } else {
+        std::u16string ok_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+            swkbd_config_common.ok_text.data(), swkbd_config_common.ok_text.size());
+
+        std::u16string header_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+            swkbd_config_common.header_text.data(), swkbd_config_common.header_text.size());
+
+        std::u16string sub_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+            swkbd_config_common.sub_text.data(), swkbd_config_common.sub_text.size());
+
+        std::u16string guide_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+            swkbd_config_common.guide_text.data(), swkbd_config_common.guide_text.size());
+
+        const u32 max_text_length =
+            swkbd_config_common.max_text_length > 0 &&
+                    swkbd_config_common.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+                ? swkbd_config_common.max_text_length
+                : DEFAULT_MAX_TEXT_LENGTH;
+
+        const u32 min_text_length = swkbd_config_common.min_text_length <= max_text_length
+                                        ? swkbd_config_common.min_text_length
+                                        : 0;
+
+        const s32 initial_cursor_position = [this] {
+            switch (swkbd_config_common.initial_cursor_position) {
+            case SwkbdInitialCursorPosition::Start:
+            default:
+                return 0;
+            case SwkbdInitialCursorPosition::End:
+                return static_cast<s32>(initial_text.size());
+            }
+        }();
+
+        const auto text_draw_type = [this, max_text_length] {
+            switch (swkbd_config_common.text_draw_type) {
+            case SwkbdTextDrawType::Line:
+            default:
+                return max_text_length <= 32 ? SwkbdTextDrawType::Line : SwkbdTextDrawType::Box;
+            case SwkbdTextDrawType::Box:
+            case SwkbdTextDrawType::DownloadCode:
+                return swkbd_config_common.text_draw_type;
+            }
+        }();
+
+        const auto enable_return_button = text_draw_type == SwkbdTextDrawType::Box
+                                              ? swkbd_config_common.enable_return_button
+                                              : false;
+
+        const auto disable_cancel_button = swkbd_applet_version >= SwkbdAppletVersion::Version393227
+                                               ? swkbd_config_new.disable_cancel_button
+                                               : false;
+
+        Core::Frontend::KeyboardInitializeParameters initialize_parameters{
+            .ok_text{ok_text},
+            .header_text{header_text},
+            .sub_text{sub_text},
+            .guide_text{guide_text},
+            .initial_text{initial_text},
+            .max_text_length{max_text_length},
+            .min_text_length{min_text_length},
+            .initial_cursor_position{initial_cursor_position},
+            .type{swkbd_config_common.type},
+            .password_mode{swkbd_config_common.password_mode},
+            .text_draw_type{text_draw_type},
+            .key_disable_flags{swkbd_config_common.key_disable_flags},
+            .use_blur_background{swkbd_config_common.use_blur_background},
+            .enable_backspace_button{true},
+            .enable_return_button{enable_return_button},
+            .disable_cancel_button{disable_cancel_button},
+        };
+
+        frontend.InitializeKeyboard(false, std::move(initialize_parameters),
+                                    [this](SwkbdResult result, std::u16string submitted_text) {
+                                        SubmitTextNormal(result, submitted_text);
+                                    },
+                                    {});
+    }
+}
+
+void SoftwareKeyboard::ShowNormalKeyboard() {
+    frontend.ShowNormalKeyboard();
+}
+
+void SoftwareKeyboard::ShowTextCheckDialog(SwkbdTextCheckResult text_check_result,
+                                           std::u16string text_check_message) {
+    frontend.ShowTextCheckDialog(text_check_result, text_check_message);
+}
+
+void SoftwareKeyboard::ShowInlineKeyboard() {
+    if (swkbd_state != SwkbdState::InitializedIsHidden) {
+        return;
+    }
+
+    ChangeState(SwkbdState::InitializedIsAppearing);
+
+    const auto& appear_arg = swkbd_calc_arg.appear_arg;
+
+    const u32 max_text_length =
+        appear_arg.max_text_length > 0 && appear_arg.max_text_length <= DEFAULT_MAX_TEXT_LENGTH
+            ? appear_arg.max_text_length
+            : DEFAULT_MAX_TEXT_LENGTH;
+
+    const u32 min_text_length =
+        appear_arg.min_text_length <= max_text_length ? appear_arg.min_text_length : 0;
+
+    Core::Frontend::InlineAppearParameters appear_parameters{
+        .max_text_length{max_text_length},
+        .min_text_length{min_text_length},
+        .key_top_scale_x{swkbd_calc_arg.key_top_scale_x},
+        .key_top_scale_y{swkbd_calc_arg.key_top_scale_y},
+        .key_top_translate_x{swkbd_calc_arg.key_top_translate_x},
+        .key_top_translate_y{swkbd_calc_arg.key_top_translate_y},
+        .type{appear_arg.type},
+        .key_disable_flags{appear_arg.key_disable_flags},
+        .key_top_as_floating{swkbd_calc_arg.key_top_as_floating},
+        .enable_backspace_button{swkbd_calc_arg.enable_backspace_button},
+        .enable_return_button{appear_arg.enable_return_button},
+        .disable_cancel_button{appear_arg.disable_cancel_button},
+    };
+
+    frontend.ShowInlineKeyboard(std::move(appear_parameters));
+
+    ChangeState(SwkbdState::InitializedIsShown);
+}
+
+void SoftwareKeyboard::HideInlineKeyboard() {
+    if (swkbd_state != SwkbdState::InitializedIsShown) {
+        return;
+    }
+
+    ChangeState(SwkbdState::InitializedIsDisappearing);
+
+    frontend.HideInlineKeyboard();
+
+    ChangeState(SwkbdState::InitializedIsHidden);
+}
+
+void SoftwareKeyboard::InlineTextChanged() {
+    Core::Frontend::InlineTextParameters text_parameters{
+        .input_text{current_text},
+        .cursor_position{current_cursor_position},
+    };
+
+    frontend.InlineTextChanged(std::move(text_parameters));
+}
+
+void SoftwareKeyboard::ExitKeyboard() {
+    complete = true;
+    status = RESULT_SUCCESS;
+
+    frontend.ExitKeyboard();
+
+    broker.SignalStateChanged();
+}
+
+// Inline Software Keyboard Requests
+
+void SoftwareKeyboard::RequestFinalize(const std::vector<u8>& request_data) {
+    LOG_DEBUG(Service_AM, "Processing Request: Finalize");
+
+    ChangeState(SwkbdState::NotInitialized);
+
+    ExitKeyboard();
+}
+
+void SoftwareKeyboard::RequestSetUserWordInfo(const std::vector<u8>& request_data) {
+    LOG_WARNING(Service_AM, "SetUserWordInfo is not implemented.");
+}
+
+void SoftwareKeyboard::RequestSetCustomizeDic(const std::vector<u8>& request_data) {
+    LOG_WARNING(Service_AM, "SetCustomizeDic is not implemented.");
+}
+
+void SoftwareKeyboard::RequestCalc(const std::vector<u8>& request_data) {
+    LOG_DEBUG(Service_AM, "Processing Request: Calc");
+
+    ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + sizeof(SwkbdCalcArg));
+
+    std::memcpy(&swkbd_calc_arg, request_data.data() + sizeof(SwkbdRequestCommand),
+                sizeof(SwkbdCalcArg));
+
+    if (swkbd_calc_arg.flags.set_input_text) {
+        current_text = Common::UTF16StringFromFixedZeroTerminatedBuffer(
+            swkbd_calc_arg.input_text.data(), swkbd_calc_arg.input_text.size());
+    }
+
+    if (swkbd_calc_arg.flags.set_cursor_position) {
+        current_cursor_position = swkbd_calc_arg.cursor_position;
+    }
+
+    if (swkbd_calc_arg.flags.set_utf8_mode) {
+        inline_use_utf8 = swkbd_calc_arg.utf8_mode;
+    }
+
+    if (swkbd_state <= SwkbdState::InitializedIsHidden &&
+        swkbd_calc_arg.flags.unset_customize_dic) {
+        ReplyUnsetCustomizeDic();
     }
     }
+
+    if (swkbd_state <= SwkbdState::InitializedIsHidden &&
+        swkbd_calc_arg.flags.unset_user_word_info) {
+        ReplyReleasedUserWordInfo();
+    }
+
+    if (swkbd_state == SwkbdState::NotInitialized && swkbd_calc_arg.flags.set_initialize_arg) {
+        InitializeFrontendKeyboard();
+
+        ChangeState(SwkbdState::InitializedIsHidden);
+
+        ReplyFinishedInitialize();
+    }
+
+    if (!swkbd_calc_arg.flags.set_initialize_arg &&
+        (swkbd_calc_arg.flags.set_input_text || swkbd_calc_arg.flags.set_cursor_position)) {
+        InlineTextChanged();
+    }
+
+    if (swkbd_state == SwkbdState::InitializedIsHidden && swkbd_calc_arg.flags.appear) {
+        ShowInlineKeyboard();
+        return;
+    }
+
+    if (swkbd_state == SwkbdState::InitializedIsShown && swkbd_calc_arg.flags.disappear) {
+        HideInlineKeyboard();
+        return;
+    }
+}
+
+void SoftwareKeyboard::RequestSetCustomizedDictionaries(const std::vector<u8>& request_data) {
+    LOG_WARNING(Service_AM, "SetCustomizedDictionaries is not implemented.");
+}
+
+void SoftwareKeyboard::RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data) {
+    LOG_WARNING(Service_AM, "(STUBBED) Processing Request: UnsetCustomizedDictionaries");
+
+    ReplyUnsetCustomizedDictionaries();
+}
+
+void SoftwareKeyboard::RequestSetChangedStringV2Flag(const std::vector<u8>& request_data) {
+    LOG_DEBUG(Service_AM, "Processing Request: SetChangedStringV2Flag");
+
+    ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1);
+
+    std::memcpy(&use_changed_string_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1);
+}
+
+void SoftwareKeyboard::RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data) {
+    LOG_DEBUG(Service_AM, "Processing Request: SetMovedCursorV2Flag");
+
+    ASSERT(request_data.size() == sizeof(SwkbdRequestCommand) + 1);
+
+    std::memcpy(&use_moved_cursor_v2, request_data.data() + sizeof(SwkbdRequestCommand), 1);
+}
+
+// Inline Software Keyboard Replies
+
+void SoftwareKeyboard::ReplyFinishedInitialize() {
+    LOG_DEBUG(Service_AM, "Sending Reply: FinishedInitialize");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + 1);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::FinishedInitialize);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyDefault() {
+    LOG_DEBUG(Service_AM, "Sending Reply: Default");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::Default);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyChangedString() {
+    LOG_DEBUG(Service_AM, "Sending Reply: ChangedString");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedString);
+
+    const SwkbdChangedStringArg changed_string_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .dictionary_start_cursor_position{-1},
+        .dictionary_end_cursor_position{-1},
+        .cursor_position{current_cursor_position},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
+                current_text.size() * sizeof(char16_t));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg,
+                sizeof(SwkbdChangedStringArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyMovedCursor() {
+    LOG_DEBUG(Service_AM, "Sending Reply: MovedCursor");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursor);
+
+    const SwkbdMovedCursorArg moved_cursor_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .cursor_position{current_cursor_position},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
+                current_text.size() * sizeof(char16_t));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg,
+                sizeof(SwkbdMovedCursorArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyMovedTab() {
+    LOG_DEBUG(Service_AM, "Sending Reply: MovedTab");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedTabArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedTab);
+
+    const SwkbdMovedTabArg moved_tab_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .cursor_position{current_cursor_position},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
+                current_text.size() * sizeof(char16_t));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_tab_arg,
+                sizeof(SwkbdMovedTabArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyDecidedEnter() {
+    LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnter");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdDecidedEnterArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnter);
+
+    const SwkbdDecidedEnterArg decided_enter_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
+                current_text.size() * sizeof(char16_t));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &decided_enter_arg,
+                sizeof(SwkbdDecidedEnterArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+
+    HideInlineKeyboard();
+}
+
+void SoftwareKeyboard::ReplyDecidedCancel() {
+    LOG_DEBUG(Service_AM, "Sending Reply: DecidedCancel");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedCancel);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+
+    HideInlineKeyboard();
+}
+
+void SoftwareKeyboard::ReplyChangedStringUtf8() {
+    LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8);
+
+    std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
+
+    const SwkbdChangedStringArg changed_string_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .dictionary_start_cursor_position{-1},
+        .dictionary_end_cursor_position{-1},
+        .cursor_position{current_cursor_position},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg,
+                sizeof(SwkbdChangedStringArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
 }
 }
+
+void SoftwareKeyboard::ReplyMovedCursorUtf8() {
+    LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8);
+
+    std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
+
+    const SwkbdMovedCursorArg moved_cursor_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .cursor_position{current_cursor_position},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg,
+                sizeof(SwkbdMovedCursorArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyDecidedEnterUtf8() {
+    LOG_DEBUG(Service_AM, "Sending Reply: DecidedEnterUtf8");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdDecidedEnterArg));
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::DecidedEnterUtf8);
+
+    std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
+
+    const SwkbdDecidedEnterArg decided_enter_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+    };
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &decided_enter_arg,
+                sizeof(SwkbdDecidedEnterArg));
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+
+    HideInlineKeyboard();
+}
+
+void SoftwareKeyboard::ReplyUnsetCustomizeDic() {
+    LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizeDic");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizeDic);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyReleasedUserWordInfo() {
+    LOG_DEBUG(Service_AM, "Sending Reply: ReleasedUserWordInfo");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::ReleasedUserWordInfo);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyUnsetCustomizedDictionaries() {
+    LOG_DEBUG(Service_AM, "Sending Reply: UnsetCustomizedDictionaries");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::UnsetCustomizedDictionaries);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyChangedStringV2() {
+    LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringV2");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg) + 1);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringV2);
+
+    const SwkbdChangedStringArg changed_string_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .dictionary_start_cursor_position{-1},
+        .dictionary_end_cursor_position{-1},
+        .cursor_position{current_cursor_position},
+    };
+
+    constexpr u8 flag = 0;
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
+                current_text.size() * sizeof(char16_t));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &changed_string_arg,
+                sizeof(SwkbdChangedStringArg));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdChangedStringArg),
+                &flag, 1);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyMovedCursorV2() {
+    LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorV2");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg) + 1);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorV2);
+
+    const SwkbdMovedCursorArg moved_cursor_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .cursor_position{current_cursor_position},
+    };
+
+    constexpr u8 flag = 0;
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, current_text.data(),
+                current_text.size() * sizeof(char16_t));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE, &moved_cursor_arg,
+                sizeof(SwkbdMovedCursorArg));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF16_SIZE + sizeof(SwkbdMovedCursorArg),
+                &flag, 1);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyChangedStringUtf8V2() {
+    LOG_DEBUG(Service_AM, "Sending Reply: ChangedStringUtf8V2");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg) + 1);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::ChangedStringUtf8V2);
+
+    std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
+
+    const SwkbdChangedStringArg changed_string_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .dictionary_start_cursor_position{-1},
+        .dictionary_end_cursor_position{-1},
+        .cursor_position{current_cursor_position},
+    };
+
+    constexpr u8 flag = 0;
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &changed_string_arg,
+                sizeof(SwkbdChangedStringArg));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdChangedStringArg),
+                &flag, 1);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
+void SoftwareKeyboard::ReplyMovedCursorUtf8V2() {
+    LOG_DEBUG(Service_AM, "Sending Reply: MovedCursorUtf8V2");
+
+    std::vector<u8> reply(REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg) + 1);
+
+    SetReplyBase(reply, swkbd_state, SwkbdReplyType::MovedCursorUtf8V2);
+
+    std::string utf8_current_text = Common::UTF16ToUTF8(current_text);
+
+    const SwkbdMovedCursorArg moved_cursor_arg{
+        .text_length{static_cast<u32>(current_text.size())},
+        .cursor_position{current_cursor_position},
+    };
+
+    constexpr u8 flag = 0;
+
+    std::memcpy(reply.data() + REPLY_BASE_SIZE, utf8_current_text.data(), utf8_current_text.size());
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE, &moved_cursor_arg,
+                sizeof(SwkbdMovedCursorArg));
+    std::memcpy(reply.data() + REPLY_BASE_SIZE + REPLY_UTF8_SIZE + sizeof(SwkbdMovedCursorArg),
+                &flag, 1);
+
+    broker.PushInteractiveDataFromApplet(std::make_shared<IStorage>(system, std::move(reply)));
+}
+
 } // namespace Service::AM::Applets
 } // namespace Service::AM::Applets

+ 134 - 53
src/core/hle/service/am/applets/software_keyboard.h

@@ -1,20 +1,14 @@
-// Copyright 2018 yuzu emulator team
+// Copyright 2021 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
 #pragma once
 #pragma once
 
 
-#include <array>
-#include <string>
-#include <vector>
-
 #include "common/common_funcs.h"
 #include "common/common_funcs.h"
 #include "common/common_types.h"
 #include "common/common_types.h"
-#include "common/swap.h"
-#include "core/hle/service/am/am.h"
+#include "core/hle/result.h"
 #include "core/hle/service/am/applets/applets.h"
 #include "core/hle/service/am/applets/applets.h"
-
-union ResultCode;
+#include "core/hle/service/am/applets/software_keyboard_types.h"
 
 
 namespace Core {
 namespace Core {
 class System;
 class System;
@@ -22,45 +16,10 @@ class System;
 
 
 namespace Service::AM::Applets {
 namespace Service::AM::Applets {
 
 
-enum class KeysetDisable : u32 {
-    Space = 0x02,
-    Address = 0x04,
-    Percent = 0x08,
-    Slashes = 0x10,
-    Numbers = 0x40,
-    DownloadCode = 0x80,
-};
-
-struct KeyboardConfig {
-    INSERT_PADDING_BYTES(4);
-    std::array<char16_t, 9> submit_text;
-    u16_le left_symbol_key;
-    u16_le right_symbol_key;
-    INSERT_PADDING_BYTES(1);
-    KeysetDisable keyset_disable_bitmask;
-    u32_le initial_cursor_position;
-    std::array<char16_t, 65> header_text;
-    std::array<char16_t, 129> sub_text;
-    std::array<char16_t, 257> guide_text;
-    u32_le length_limit;
-    INSERT_PADDING_BYTES(4);
-    u32_le is_password;
-    INSERT_PADDING_BYTES(5);
-    bool utf_8;
-    bool draw_background;
-    u32_le initial_string_offset;
-    u32_le initial_string_size;
-    u32_le user_dictionary_offset;
-    u32_le user_dictionary_size;
-    bool text_check;
-    u64_le text_check_callback;
-};
-static_assert(sizeof(KeyboardConfig) == 0x3E0, "KeyboardConfig has incorrect size.");
-
 class SoftwareKeyboard final : public Applet {
 class SoftwareKeyboard final : public Applet {
 public:
 public:
-    explicit SoftwareKeyboard(Core::System& system_,
-                              const Core::Frontend::SoftwareKeyboardApplet& frontend_);
+    explicit SoftwareKeyboard(Core::System& system_, LibraryAppletMode applet_mode_,
+                              Core::Frontend::SoftwareKeyboardApplet& frontend_);
     ~SoftwareKeyboard() override;
     ~SoftwareKeyboard() override;
 
 
     void Initialize() override;
     void Initialize() override;
@@ -70,17 +29,139 @@ public:
     void ExecuteInteractive() override;
     void ExecuteInteractive() override;
     void Execute() override;
     void Execute() override;
 
 
-    void WriteText(std::optional<std::u16string> text);
+    /**
+     * Submits the input text to the application.
+     * If text checking is enabled, the application will verify the input text.
+     * If use_utf8 is enabled, the input text will be converted to UTF-8 prior to being submitted.
+     * This should only be used by the normal software keyboard.
+     *
+     * @param result SwkbdResult enum
+     * @param submitted_text UTF-16 encoded string
+     */
+    void SubmitTextNormal(SwkbdResult result, std::u16string submitted_text);
+
+    /**
+     * Submits the input text to the application.
+     * If utf8_mode is enabled, the input text will be converted to UTF-8 prior to being submitted.
+     * This should only be used by the inline software keyboard.
+     *
+     * @param reply_type SwkbdReplyType enum
+     * @param submitted_text UTF-16 encoded string
+     * @param cursor_position The current position of the text cursor
+     */
+    void SubmitTextInline(SwkbdReplyType reply_type, std::u16string submitted_text,
+                          s32 cursor_position);
 
 
 private:
 private:
-    const Core::Frontend::SoftwareKeyboardApplet& frontend;
+    /// Initializes the normal software keyboard.
+    void InitializeForeground();
 
 
-    KeyboardConfig config;
-    std::u16string initial_text;
-    bool complete = false;
-    bool is_inline = false;
-    std::vector<u8> final_data;
+    /// Initializes the inline software keyboard.
+    void InitializeBackground(LibraryAppletMode applet_mode);
+
+    /// Processes the text check sent by the application.
+    void ProcessTextCheck();
+
+    /// Processes the inline software keyboard request command sent by the application.
+    void ProcessInlineKeyboardRequest();
+
+    /// Submits the input text and exits the applet.
+    void SubmitNormalOutputAndExit(SwkbdResult result, std::u16string submitted_text);
+
+    /// Submits the input text for text checking.
+    void SubmitForTextCheck(std::u16string submitted_text);
+
+    /// Sends a reply to the application after processing a request command.
+    void SendReply(SwkbdReplyType reply_type);
+
+    /// Changes the inline keyboard state.
+    void ChangeState(SwkbdState state);
+
+    /**
+     * Signals the frontend to initialize the software keyboard with common parameters.
+     * This initializes either the normal software keyboard or the inline software keyboard
+     * depending on the state of is_background.
+     * Note that this does not cause the keyboard to appear.
+     * Use the respective Show*Keyboard() functions to cause the respective keyboards to appear.
+     */
+    void InitializeFrontendKeyboard();
+
+    /// Signals the frontend to show the normal software keyboard.
+    void ShowNormalKeyboard();
+
+    /// Signals the frontend to show the text check dialog.
+    void ShowTextCheckDialog(SwkbdTextCheckResult text_check_result,
+                             std::u16string text_check_message);
+
+    /// Signals the frontend to show the inline software keyboard.
+    void ShowInlineKeyboard();
+
+    /// Signals the frontend to hide the inline software keyboard.
+    void HideInlineKeyboard();
+
+    /// Signals the frontend that the current inline keyboard text has changed.
+    void InlineTextChanged();
+
+    /// Signals both the frontend and application that the software keyboard is exiting.
+    void ExitKeyboard();
+
+    // Inline Software Keyboard Requests
+
+    void RequestFinalize(const std::vector<u8>& request_data);
+    void RequestSetUserWordInfo(const std::vector<u8>& request_data);
+    void RequestSetCustomizeDic(const std::vector<u8>& request_data);
+    void RequestCalc(const std::vector<u8>& request_data);
+    void RequestSetCustomizedDictionaries(const std::vector<u8>& request_data);
+    void RequestUnsetCustomizedDictionaries(const std::vector<u8>& request_data);
+    void RequestSetChangedStringV2Flag(const std::vector<u8>& request_data);
+    void RequestSetMovedCursorV2Flag(const std::vector<u8>& request_data);
+
+    // Inline Software Keyboard Replies
+
+    void ReplyFinishedInitialize();
+    void ReplyDefault();
+    void ReplyChangedString();
+    void ReplyMovedCursor();
+    void ReplyMovedTab();
+    void ReplyDecidedEnter();
+    void ReplyDecidedCancel();
+    void ReplyChangedStringUtf8();
+    void ReplyMovedCursorUtf8();
+    void ReplyDecidedEnterUtf8();
+    void ReplyUnsetCustomizeDic();
+    void ReplyReleasedUserWordInfo();
+    void ReplyUnsetCustomizedDictionaries();
+    void ReplyChangedStringV2();
+    void ReplyMovedCursorV2();
+    void ReplyChangedStringUtf8V2();
+    void ReplyMovedCursorUtf8V2();
+
+    LibraryAppletMode applet_mode;
+    Core::Frontend::SoftwareKeyboardApplet& frontend;
     Core::System& system;
     Core::System& system;
+
+    SwkbdAppletVersion swkbd_applet_version;
+
+    SwkbdConfigCommon swkbd_config_common;
+    SwkbdConfigOld swkbd_config_old;
+    SwkbdConfigOld2 swkbd_config_old2;
+    SwkbdConfigNew swkbd_config_new;
+    std::u16string initial_text;
+
+    SwkbdState swkbd_state{SwkbdState::NotInitialized};
+    SwkbdInitializeArg swkbd_initialize_arg;
+    SwkbdCalcArg swkbd_calc_arg;
+    bool use_changed_string_v2{false};
+    bool use_moved_cursor_v2{false};
+    bool inline_use_utf8{false};
+    s32 current_cursor_position{};
+
+    std::u16string current_text;
+
+    bool is_background{false};
+
+    bool complete{false};
+    ResultCode status{RESULT_SUCCESS};
 };
 };
 
 
 } // namespace Service::AM::Applets
 } // namespace Service::AM::Applets

+ 295 - 0
src/core/hle/service/am/applets/software_keyboard_types.h

@@ -0,0 +1,295 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace Service::AM::Applets {
+
+constexpr std::size_t MAX_OK_TEXT_LENGTH = 8;
+constexpr std::size_t MAX_HEADER_TEXT_LENGTH = 64;
+constexpr std::size_t MAX_SUB_TEXT_LENGTH = 128;
+constexpr std::size_t MAX_GUIDE_TEXT_LENGTH = 256;
+constexpr std::size_t STRING_BUFFER_SIZE = 0x7D4;
+
+enum class SwkbdAppletVersion : u32_le {
+    Version5 = 0x5,          // 1.0.0
+    Version65542 = 0x10006,  // 2.0.0 - 2.3.0
+    Version196615 = 0x30007, // 3.0.0 - 3.0.2
+    Version262152 = 0x40008, // 4.0.0 - 4.1.0
+    Version327689 = 0x50009, // 5.0.0 - 5.1.0
+    Version393227 = 0x6000B, // 6.0.0 - 7.0.1
+    Version524301 = 0x8000D, // 8.0.0+
+};
+
+enum class SwkbdType : u32 {
+    Normal,
+    NumberPad,
+    Qwerty,
+    Unknown3,
+    Latin,
+    SimplifiedChinese,
+    TraditionalChinese,
+    Korean,
+};
+
+enum class SwkbdInitialCursorPosition : u32 {
+    Start,
+    End,
+};
+
+enum class SwkbdPasswordMode : u32 {
+    Disabled,
+    Enabled,
+};
+
+enum class SwkbdTextDrawType : u32 {
+    Line,
+    Box,
+    DownloadCode,
+};
+
+enum class SwkbdResult : u32 {
+    Ok,
+    Cancel,
+};
+
+enum class SwkbdTextCheckResult : u32 {
+    Success,
+    Failure,
+    Confirm,
+    Silent,
+};
+
+enum class SwkbdState : u32 {
+    NotInitialized = 0x0,
+    InitializedIsHidden = 0x1,
+    InitializedIsAppearing = 0x2,
+    InitializedIsShown = 0x3,
+    InitializedIsDisappearing = 0x4,
+};
+
+enum class SwkbdRequestCommand : u32 {
+    Finalize = 0x4,
+    SetUserWordInfo = 0x6,
+    SetCustomizeDic = 0x7,
+    Calc = 0xA,
+    SetCustomizedDictionaries = 0xB,
+    UnsetCustomizedDictionaries = 0xC,
+    SetChangedStringV2Flag = 0xD,
+    SetMovedCursorV2Flag = 0xE,
+};
+
+enum class SwkbdReplyType : u32 {
+    FinishedInitialize = 0x0,
+    Default = 0x1,
+    ChangedString = 0x2,
+    MovedCursor = 0x3,
+    MovedTab = 0x4,
+    DecidedEnter = 0x5,
+    DecidedCancel = 0x6,
+    ChangedStringUtf8 = 0x7,
+    MovedCursorUtf8 = 0x8,
+    DecidedEnterUtf8 = 0x9,
+    UnsetCustomizeDic = 0xA,
+    ReleasedUserWordInfo = 0xB,
+    UnsetCustomizedDictionaries = 0xC,
+    ChangedStringV2 = 0xD,
+    MovedCursorV2 = 0xE,
+    ChangedStringUtf8V2 = 0xF,
+    MovedCursorUtf8V2 = 0x10,
+};
+
+struct SwkbdKeyDisableFlags {
+    union {
+        u32 raw{};
+
+        BitField<1, 1, u32> space;
+        BitField<2, 1, u32> at;
+        BitField<3, 1, u32> percent;
+        BitField<4, 1, u32> slash;
+        BitField<5, 1, u32> backslash;
+        BitField<6, 1, u32> numbers;
+        BitField<7, 1, u32> download_code;
+        BitField<8, 1, u32> username;
+    };
+};
+static_assert(sizeof(SwkbdKeyDisableFlags) == 0x4, "SwkbdKeyDisableFlags has incorrect size.");
+
+struct SwkbdConfigCommon {
+    SwkbdType type{};
+    std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
+    char16_t left_optional_symbol_key{};
+    char16_t right_optional_symbol_key{};
+    bool use_prediction{};
+    INSERT_PADDING_BYTES(1);
+    SwkbdKeyDisableFlags key_disable_flags{};
+    SwkbdInitialCursorPosition initial_cursor_position{};
+    std::array<char16_t, MAX_HEADER_TEXT_LENGTH + 1> header_text{};
+    std::array<char16_t, MAX_SUB_TEXT_LENGTH + 1> sub_text{};
+    std::array<char16_t, MAX_GUIDE_TEXT_LENGTH + 1> guide_text{};
+    u32 max_text_length{};
+    u32 min_text_length{};
+    SwkbdPasswordMode password_mode{};
+    SwkbdTextDrawType text_draw_type{};
+    bool enable_return_button{};
+    bool use_utf8{};
+    bool use_blur_background{};
+    INSERT_PADDING_BYTES(1);
+    u32 initial_string_offset{};
+    u32 initial_string_length{};
+    u32 user_dictionary_offset{};
+    u32 user_dictionary_entries{};
+    bool use_text_check{};
+    INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(SwkbdConfigCommon) == 0x3D4, "SwkbdConfigCommon has incorrect size.");
+
+#pragma pack(push, 4)
+// SwkbdAppletVersion 0x5, 0x10006
+struct SwkbdConfigOld {
+    INSERT_PADDING_WORDS(1);
+    VAddr text_check_callback{};
+};
+static_assert(sizeof(SwkbdConfigOld) == 0x3E0 - sizeof(SwkbdConfigCommon),
+              "SwkbdConfigOld has incorrect size.");
+
+// SwkbdAppletVersion 0x30007, 0x40008, 0x50009
+struct SwkbdConfigOld2 {
+    INSERT_PADDING_WORDS(1);
+    VAddr text_check_callback{};
+    std::array<u32, 8> text_grouping{};
+};
+static_assert(sizeof(SwkbdConfigOld2) == 0x400 - sizeof(SwkbdConfigCommon),
+              "SwkbdConfigOld2 has incorrect size.");
+
+// SwkbdAppletVersion 0x6000B, 0x8000D
+struct SwkbdConfigNew {
+    std::array<u32, 8> text_grouping{};
+    std::array<u64, 24> customized_dictionary_set_entries{};
+    u8 total_customized_dictionary_set_entries{};
+    bool disable_cancel_button{};
+    INSERT_PADDING_BYTES(18);
+};
+static_assert(sizeof(SwkbdConfigNew) == 0x4C8 - sizeof(SwkbdConfigCommon),
+              "SwkbdConfigNew has incorrect size.");
+#pragma pack(pop)
+
+struct SwkbdTextCheck {
+    SwkbdTextCheckResult text_check_result{};
+    std::array<char16_t, STRING_BUFFER_SIZE / 2> text_check_message{};
+};
+static_assert(sizeof(SwkbdTextCheck) == 0x7D8, "SwkbdTextCheck has incorrect size.");
+
+struct SwkbdCalcArgFlags {
+    union {
+        u64 raw{};
+
+        BitField<0, 1, u64> set_initialize_arg;
+        BitField<1, 1, u64> set_volume;
+        BitField<2, 1, u64> appear;
+        BitField<3, 1, u64> set_input_text;
+        BitField<4, 1, u64> set_cursor_position;
+        BitField<5, 1, u64> set_utf8_mode;
+        BitField<6, 1, u64> unset_customize_dic;
+        BitField<7, 1, u64> disappear;
+        BitField<8, 1, u64> unknown;
+        BitField<9, 1, u64> set_key_top_translate_scale;
+        BitField<10, 1, u64> unset_user_word_info;
+        BitField<11, 1, u64> set_disable_hardware_keyboard;
+    };
+};
+static_assert(sizeof(SwkbdCalcArgFlags) == 0x8, "SwkbdCalcArgFlags has incorrect size.");
+
+struct SwkbdInitializeArg {
+    u32 unknown{};
+    bool library_applet_mode_flag{};
+    bool is_above_hos_500{};
+    INSERT_PADDING_BYTES(2);
+};
+static_assert(sizeof(SwkbdInitializeArg) == 0x8, "SwkbdInitializeArg has incorrect size.");
+
+struct SwkbdAppearArg {
+    SwkbdType type{};
+    std::array<char16_t, MAX_OK_TEXT_LENGTH + 1> ok_text{};
+    char16_t left_optional_symbol_key{};
+    char16_t right_optional_symbol_key{};
+    bool use_prediction{};
+    bool disable_cancel_button{};
+    SwkbdKeyDisableFlags key_disable_flags{};
+    u32 max_text_length{};
+    u32 min_text_length{};
+    bool enable_return_button{};
+    INSERT_PADDING_BYTES(3);
+    u32 flags{};
+    INSERT_PADDING_WORDS(6);
+};
+static_assert(sizeof(SwkbdAppearArg) == 0x48, "SwkbdAppearArg has incorrect size.");
+
+struct SwkbdCalcArg {
+    u32 unknown{};
+    u16 calc_arg_size{};
+    INSERT_PADDING_BYTES(2);
+    SwkbdCalcArgFlags flags{};
+    SwkbdInitializeArg initialize_arg{};
+    f32 volume{};
+    s32 cursor_position{};
+    SwkbdAppearArg appear_arg{};
+    std::array<char16_t, 0x1FA> input_text{};
+    bool utf8_mode{};
+    INSERT_PADDING_BYTES(1);
+    bool enable_backspace_button{};
+    INSERT_PADDING_BYTES(3);
+    bool key_top_as_floating{};
+    bool footer_scalable{};
+    bool alpha_enabled_in_input_mode{};
+    u8 input_mode_fade_type{};
+    bool disable_touch{};
+    bool disable_hardware_keyboard{};
+    INSERT_PADDING_BYTES(8);
+    f32 key_top_scale_x{};
+    f32 key_top_scale_y{};
+    f32 key_top_translate_x{};
+    f32 key_top_translate_y{};
+    f32 key_top_bg_alpha{};
+    f32 footer_bg_alpha{};
+    f32 balloon_scale{};
+    INSERT_PADDING_WORDS(4);
+    u8 se_group{};
+    INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(SwkbdCalcArg) == 0x4A0, "SwkbdCalcArg has incorrect size.");
+
+struct SwkbdChangedStringArg {
+    u32 text_length{};
+    s32 dictionary_start_cursor_position{};
+    s32 dictionary_end_cursor_position{};
+    s32 cursor_position{};
+};
+static_assert(sizeof(SwkbdChangedStringArg) == 0x10, "SwkbdChangedStringArg has incorrect size.");
+
+struct SwkbdMovedCursorArg {
+    u32 text_length{};
+    s32 cursor_position{};
+};
+static_assert(sizeof(SwkbdMovedCursorArg) == 0x8, "SwkbdMovedCursorArg has incorrect size.");
+
+struct SwkbdMovedTabArg {
+    u32 text_length{};
+    s32 cursor_position{};
+};
+static_assert(sizeof(SwkbdMovedTabArg) == 0x8, "SwkbdMovedTabArg has incorrect size.");
+
+struct SwkbdDecidedEnterArg {
+    u32 text_length{};
+};
+static_assert(sizeof(SwkbdDecidedEnterArg) == 0x4, "SwkbdDecidedEnterArg has incorrect size.");
+
+} // namespace Service::AM::Applets

+ 3 - 2
src/core/hle/service/am/applets/web_browser.cpp

@@ -208,8 +208,9 @@ void ExtractSharedFonts(Core::System& system) {
 
 
 } // namespace
 } // namespace
 
 
-WebBrowser::WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_)
-    : Applet{system_.Kernel()}, frontend(frontend_), system{system_} {}
+WebBrowser::WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_,
+                       const Core::Frontend::WebBrowserApplet& frontend_)
+    : Applet{system_.Kernel()}, applet_mode{applet_mode_}, frontend(frontend_), system{system_} {}
 
 
 WebBrowser::~WebBrowser() = default;
 WebBrowser::~WebBrowser() = default;
 
 

+ 3 - 1
src/core/hle/service/am/applets/web_browser.h

@@ -25,7 +25,8 @@ namespace Service::AM::Applets {
 
 
 class WebBrowser final : public Applet {
 class WebBrowser final : public Applet {
 public:
 public:
-    WebBrowser(Core::System& system_, const Core::Frontend::WebBrowserApplet& frontend_);
+    WebBrowser(Core::System& system_, LibraryAppletMode applet_mode_,
+               const Core::Frontend::WebBrowserApplet& frontend_);
 
 
     ~WebBrowser() override;
     ~WebBrowser() override;
 
 
@@ -63,6 +64,7 @@ private:
     void ExecuteWifi();
     void ExecuteWifi();
     void ExecuteLobby();
     void ExecuteLobby();
 
 
+    LibraryAppletMode applet_mode;
     const Core::Frontend::WebBrowserApplet& frontend;
     const Core::Frontend::WebBrowserApplet& frontend;
 
 
     bool complete{false};
     bool complete{false};

+ 4 - 0
src/yuzu/CMakeLists.txt

@@ -18,6 +18,7 @@ add_executable(yuzu
     applets/profile_select.h
     applets/profile_select.h
     applets/software_keyboard.cpp
     applets/software_keyboard.cpp
     applets/software_keyboard.h
     applets/software_keyboard.h
+    applets/software_keyboard.ui
     applets/web_browser.cpp
     applets/web_browser.cpp
     applets/web_browser.h
     applets/web_browser.h
     bootmanager.cpp
     bootmanager.cpp
@@ -143,6 +144,9 @@ add_executable(yuzu
     uisettings.h
     uisettings.h
     util/limitable_input_dialog.cpp
     util/limitable_input_dialog.cpp
     util/limitable_input_dialog.h
     util/limitable_input_dialog.h
+    util/overlay_dialog.cpp
+    util/overlay_dialog.h
+    util/overlay_dialog.ui
     util/sequence_dialog/sequence_dialog.cpp
     util/sequence_dialog/sequence_dialog.cpp
     util/sequence_dialog/sequence_dialog.h
     util/sequence_dialog/sequence_dialog.h
     util/url_request_interceptor.cpp
     util/url_request_interceptor.cpp

+ 12 - 10
src/yuzu/applets/error.cpp

@@ -19,11 +19,11 @@ QtErrorDisplay::~QtErrorDisplay() = default;
 void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
 void QtErrorDisplay::ShowError(ResultCode error, std::function<void()> finished) const {
     callback = std::move(finished);
     callback = std::move(finished);
     emit MainWindowDisplayError(
     emit MainWindowDisplayError(
-        tr("An error has occurred.\nPlease try again or contact the developer of the "
-           "software.\n\nError Code: %1-%2 (0x%3)")
+        tr("Error Code: %1-%2 (0x%3)")
             .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
             .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
             .arg(error.description, 4, 10, QChar::fromLatin1('0'))
             .arg(error.description, 4, 10, QChar::fromLatin1('0'))
-            .arg(error.raw, 8, 16, QChar::fromLatin1('0')));
+            .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
+        tr("An error has occurred.\nPlease try again or contact the developer of the software."));
 }
 }
 
 
 void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
 void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::seconds time,
@@ -32,13 +32,14 @@ void QtErrorDisplay::ShowErrorWithTimestamp(ResultCode error, std::chrono::secon
 
 
     const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
     const QDateTime date_time = QDateTime::fromSecsSinceEpoch(time.count());
     emit MainWindowDisplayError(
     emit MainWindowDisplayError(
-        tr("An error occurred on %1 at %2.\nPlease try again or contact the "
-           "developer of the software.\n\nError Code: %3-%4 (0x%5)")
-            .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
-            .arg(date_time.toString(QStringLiteral("h:mm:ss A")))
+        tr("Error Code: %1-%2 (0x%3)")
             .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
             .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
             .arg(error.description, 4, 10, QChar::fromLatin1('0'))
             .arg(error.description, 4, 10, QChar::fromLatin1('0'))
-            .arg(error.raw, 8, 16, QChar::fromLatin1('0')));
+            .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
+        tr("An error occurred on %1 at %2.\nPlease try again or contact the developer of the "
+           "software.")
+            .arg(date_time.toString(QStringLiteral("dddd, MMMM d, yyyy")))
+            .arg(date_time.toString(QStringLiteral("h:mm:ss A"))));
 }
 }
 
 
 void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text,
 void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_text,
@@ -46,10 +47,11 @@ void QtErrorDisplay::ShowCustomErrorText(ResultCode error, std::string dialog_te
                                          std::function<void()> finished) const {
                                          std::function<void()> finished) const {
     callback = std::move(finished);
     callback = std::move(finished);
     emit MainWindowDisplayError(
     emit MainWindowDisplayError(
-        tr("An error has occurred.\nError Code: %1-%2 (0x%3)\n\n%4\n\n%5")
+        tr("Error Code: %1-%2 (0x%3)")
             .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
             .arg(static_cast<u32>(error.module.Value()) + 2000, 4, 10, QChar::fromLatin1('0'))
             .arg(error.description, 4, 10, QChar::fromLatin1('0'))
             .arg(error.description, 4, 10, QChar::fromLatin1('0'))
-            .arg(error.raw, 8, 16, QChar::fromLatin1('0'))
+            .arg(error.raw, 8, 16, QChar::fromLatin1('0')),
+        tr("An error has occurred.\n\n%1\n\n%2")
             .arg(QString::fromStdString(dialog_text))
             .arg(QString::fromStdString(dialog_text))
             .arg(QString::fromStdString(fullscreen_text)));
             .arg(QString::fromStdString(fullscreen_text)));
 }
 }

+ 1 - 1
src/yuzu/applets/error.h

@@ -24,7 +24,7 @@ public:
                              std::function<void()> finished) const override;
                              std::function<void()> finished) const override;
 
 
 signals:
 signals:
-    void MainWindowDisplayError(QString error) const;
+    void MainWindowDisplayError(QString error_code, QString error_text) const;
 
 
 private:
 private:
     void MainWindowFinishedError();
     void MainWindowFinishedError();

+ 1600 - 112
src/yuzu/applets/software_keyboard.cpp

@@ -1,153 +1,1641 @@
-// Copyright 2018 yuzu Emulator Project
+// Copyright 2021 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
-#include <algorithm>
-#include <mutex>
-#include <QDialogButtonBox>
-#include <QFont>
-#include <QLabel>
-#include <QLineEdit>
-#include <QVBoxLayout>
-#include "core/hle/lock.h"
+#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/applets/software_keyboard.h"
 #include "yuzu/main.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,
+            },
+        }},
+    }};
 
 
-QtSoftwareKeyboardValidator::QtSoftwareKeyboardValidator(
-    Core::Frontend::SoftwareKeyboardParameters parameters)
-    : parameters(std::move(parameters)) {}
+    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,
+        },
+    }};
 
 
-QValidator::State QtSoftwareKeyboardValidator::validate(QString& input, int& pos) const {
-    if (input.size() > static_cast<s64>(parameters.max_length)) {
-        return Invalid;
+    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));
     }
     }
-    if (parameters.disable_space && input.contains(QLatin1Char{' '})) {
-        return Invalid;
+
+    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);
+            }
+        });
     }
     }
-    if (parameters.disable_address && input.contains(QLatin1Char{'@'})) {
-        return Invalid;
+
+    // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
+    if (system.IsPoweredOn()) {
+        input_interpreter = std::make_unique<InputInterpreter>(system);
     }
     }
-    if (parameters.disable_percent && input.contains(QLatin1Char{'%'})) {
-        return Invalid;
+}
+
+QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() {
+    StopInputThread();
+}
+
+void QtSoftwareKeyboardDialog::ShowNormalKeyboard(QPoint pos, QSize size) {
+    if (isVisible()) {
+        return;
     }
     }
-    if (parameters.disable_slash &&
-        (input.contains(QLatin1Char{'/'}) || input.contains(QLatin1Char{'\\'}))) {
-        return Invalid;
+
+    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;
     }
     }
-    if (parameters.disable_number &&
-        std::any_of(input.begin(), input.end(), [](QChar c) { return c.isDigit(); })) {
-        return Invalid;
+    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) {
+            emit SubmitNormalText(SwkbdResult::Ok, current_text);
+            break;
+        }
+
+        StartInputThread();
+        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;
 
 
-    if (parameters.disable_download_code && std::any_of(input.begin(), input.end(), [](QChar c) {
-            return c == QLatin1Char{'O'} || c == QLatin1Char{'I'};
-        })) {
-        return Invalid;
+    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.
+    if (is_inline) {
+        emit SubmitInlineText(SwkbdReplyType::DecidedCancel, current_text, cursor_position);
+    } else {
+        emit SubmitNormalText(SwkbdResult::Cancel, current_text);
     }
     }
+}
+
+void QtSoftwareKeyboardDialog::keyPressEvent(QKeyEvent* event) {
+    if (!is_inline) {
+        QDialog::keyPressEvent(event);
+        return;
+    }
+
+    const auto entered_key = event->key();
 
 
-    return Acceptable;
+    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());
 }
 }
 
 
-QtSoftwareKeyboardDialog::QtSoftwareKeyboardDialog(
-    QWidget* parent, Core::Frontend::SoftwareKeyboardParameters parameters_)
-    : QDialog(parent), parameters(std::move(parameters_)) {
-    layout = new QVBoxLayout;
-
-    header_label = new QLabel(QString::fromStdU16String(parameters.header_text));
-    header_label->setFont({header_label->font().family(), 11, QFont::Bold});
-    if (header_label->text().isEmpty())
-        header_label->setText(tr("Enter text:"));
-
-    sub_label = new QLabel(QString::fromStdU16String(parameters.sub_text));
-    sub_label->setFont({sub_label->font().family(), sub_label->font().pointSize(),
-                        sub_label->font().weight(), true});
-    sub_label->setHidden(parameters.sub_text.empty());
-
-    guide_label = new QLabel(QString::fromStdU16String(parameters.guide_text));
-    guide_label->setHidden(parameters.guide_text.empty());
-
-    length_label = new QLabel(QStringLiteral("0/%1").arg(parameters.max_length));
-    length_label->setAlignment(Qt::AlignRight);
-    length_label->setFont({length_label->font().family(), 8});
-
-    line_edit = new QLineEdit;
-    line_edit->setValidator(new QtSoftwareKeyboardValidator(parameters));
-    line_edit->setMaxLength(static_cast<int>(parameters.max_length));
-    line_edit->setText(QString::fromStdU16String(parameters.initial_text));
-    line_edit->setCursorPosition(
-        parameters.cursor_at_beginning ? 0 : static_cast<int>(parameters.initial_text.size()));
-    line_edit->setEchoMode(parameters.password ? QLineEdit::Password : QLineEdit::Normal);
-
-    connect(line_edit, &QLineEdit::textChanged, this, [this](const QString& text) {
-        length_label->setText(QStringLiteral("%1/%2").arg(text.size()).arg(parameters.max_length));
-    });
-
-    buttons = new QDialogButtonBox(QDialogButtonBox::Cancel);
-    if (parameters.submit_text.empty()) {
-        buttons->addButton(QDialogButtonBox::Ok);
+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 {
     } else {
-        buttons->addButton(QString::fromStdU16String(parameters.submit_text),
-                           QDialogButtonBox::AcceptRole);
+        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);
     }
     }
-    connect(buttons, &QDialogButtonBox::accepted, this, &QtSoftwareKeyboardDialog::accept);
-    connect(buttons, &QDialogButtonBox::rejected, this, &QtSoftwareKeyboardDialog::reject);
-    layout->addWidget(header_label);
-    layout->addWidget(sub_label);
-    layout->addWidget(guide_label);
-    layout->addWidget(length_label);
-    layout->addWidget(line_edit);
-    layout->addWidget(buttons);
-    setLayout(layout);
-    setWindowTitle(tr("Software Keyboard"));
 }
 }
 
 
-QtSoftwareKeyboardDialog::~QtSoftwareKeyboardDialog() = default;
+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;
+    }
 
 
-void QtSoftwareKeyboardDialog::accept() {
-    text = line_edit->text().toStdU16String();
-    QDialog::accept();
+    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::reject() {
-    text.clear();
-    QDialog::reject();
+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("background-image: url(:/overlay/osk_button_shift_lock_off.png);"
+                           "\nbackground-position: left top;"
+                           "\nbackground-repeat: no-repeat;"
+                           "\nbackground-origin: content;"));
+
+        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("background-image: url(:/overlay/osk_button_shift_lock_off.png);"
+                               "\nbackground-position: left top;"
+                               "\nbackground-repeat: no-repeat;"
+                               "\nbackground-origin: content;"));
+
+            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("background-image: url(:/overlay/osk_button_shift_lock_on.png);"
+                               "\nbackground-position: left top;"
+                               "\nbackground-repeat: no-repeat;"
+                               "\nbackground-origin: content;"));
+
+            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;
+    }
 }
 }
 
 
-std::u16string QtSoftwareKeyboardDialog::GetText() const {
-    return text;
+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) {
 QtSoftwareKeyboard::QtSoftwareKeyboard(GMainWindow& main_window) {
-    connect(this, &QtSoftwareKeyboard::MainWindowGetText, &main_window,
-            &GMainWindow::SoftwareKeyboardGetText, Qt::QueuedConnection);
-    connect(this, &QtSoftwareKeyboard::MainWindowTextCheckDialog, &main_window,
-            &GMainWindow::SoftwareKeyboardInvokeCheckDialog, Qt::BlockingQueuedConnection);
-    connect(&main_window, &GMainWindow::SoftwareKeyboardFinishedText, this,
-            &QtSoftwareKeyboard::MainWindowFinishedText, Qt::QueuedConnection);
+    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;
 QtSoftwareKeyboard::~QtSoftwareKeyboard() = default;
 
 
-void QtSoftwareKeyboard::RequestText(std::function<void(std::optional<std::u16string>)> out,
-                                     Core::Frontend::SoftwareKeyboardParameters parameters) const {
-    text_output = std::move(out);
-    emit MainWindowGetText(parameters);
+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::SendTextCheckDialog(std::u16string error_message,
-                                             std::function<void()> finished_check_) const {
-    finished_check = std::move(finished_check_);
-    emit MainWindowTextCheckDialog(error_message);
+void QtSoftwareKeyboard::ExitKeyboard() const {
+    emit MainWindowExitKeyboard();
 }
 }
 
 
-void QtSoftwareKeyboard::MainWindowFinishedText(std::optional<std::u16string> text) {
-    // Acquire the HLE mutex
-    std::lock_guard lock{HLE::g_hle_lock};
-    text_output(std::move(text));
+void QtSoftwareKeyboard::SubmitNormalText(Service::AM::Applets::SwkbdResult result,
+                                          std::u16string submitted_text) const {
+    submit_normal_callback(result, submitted_text);
 }
 }
 
 
-void QtSoftwareKeyboard::MainWindowFinishedCheckDialog() {
-    // Acquire the HLE mutex
-    std::lock_guard lock{HLE::g_hle_lock};
-    finished_check();
+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);
 }
 }

+ 246 - 37
src/yuzu/applets/software_keyboard.h

@@ -1,54 +1,228 @@
-// Copyright 2018 yuzu Emulator Project
+// Copyright 2021 yuzu Emulator Project
 // Licensed under GPLv2 or any later version
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 // Refer to the license.txt file included.
 
 
 #pragma once
 #pragma once
 
 
+#include <array>
+#include <atomic>
+#include <memory>
+#include <thread>
+
 #include <QDialog>
 #include <QDialog>
 #include <QValidator>
 #include <QValidator>
+
 #include "core/frontend/applets/software_keyboard.h"
 #include "core/frontend/applets/software_keyboard.h"
 
 
-class GMainWindow;
-class QDialogButtonBox;
-class QLabel;
-class QLineEdit;
-class QVBoxLayout;
-class QtSoftwareKeyboard;
+enum class HIDButton : u8;
 
 
-class QtSoftwareKeyboardValidator final : public QValidator {
-public:
-    explicit QtSoftwareKeyboardValidator(Core::Frontend::SoftwareKeyboardParameters parameters);
-    State validate(QString& input, int& pos) const override;
+class InputInterpreter;
 
 
-private:
-    Core::Frontend::SoftwareKeyboardParameters parameters;
-};
+namespace Core {
+class System;
+}
+
+namespace Ui {
+class QtSoftwareKeyboardDialog;
+}
+
+class GMainWindow;
 
 
 class QtSoftwareKeyboardDialog final : public QDialog {
 class QtSoftwareKeyboardDialog final : public QDialog {
     Q_OBJECT
     Q_OBJECT
 
 
 public:
 public:
-    QtSoftwareKeyboardDialog(QWidget* parent,
-                             Core::Frontend::SoftwareKeyboardParameters parameters);
+    QtSoftwareKeyboardDialog(QWidget* parent, Core::System& system_, bool is_inline_,
+                             Core::Frontend::KeyboardInitializeParameters initialize_parameters_);
     ~QtSoftwareKeyboardDialog() override;
     ~QtSoftwareKeyboardDialog() override;
 
 
-    void accept() override;
+    void ShowNormalKeyboard(QPoint pos, QSize size);
+
+    void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+                             std::u16string text_check_message);
+
+    void ShowInlineKeyboard(Core::Frontend::InlineAppearParameters appear_parameters, QPoint pos,
+                            QSize size);
+
+    void HideInlineKeyboard();
+
+    void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
+
+    void ExitKeyboard();
+
+signals:
+    void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
+                          std::u16string submitted_text) const;
+
+    void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
+                          std::u16string submitted_text, s32 cursor_position) const;
+
+public slots:
+    void open() override;
     void reject() override;
     void reject() override;
 
 
-    std::u16string GetText() const;
+protected:
+    /// We override the keyPressEvent for inputting text into the inline software keyboard.
+    void keyPressEvent(QKeyEvent* event) override;
 
 
 private:
 private:
-    std::u16string text;
+    enum class Direction {
+        Left,
+        Up,
+        Right,
+        Down,
+    };
+
+    enum class BottomOSKIndex {
+        LowerCase,
+        UpperCase,
+        NumberPad,
+    };
+
+    /**
+     * Moves and resizes the window to a specified position and size.
+     *
+     * @param pos Top-left window position
+     * @param size Window size
+     */
+    void MoveAndResizeWindow(QPoint pos, QSize size);
+
+    /**
+     * Rescales all keyboard elements to account for High DPI displays.
+     *
+     * @param width Window width
+     * @param height Window height
+     * @param dpi_scale Display scaling factor
+     */
+    void RescaleKeyboardElements(float width, float height, float dpi_scale);
+
+    /// Sets the keyboard type based on initialize_parameters.
+    void SetKeyboardType();
+
+    /// Sets the password mode based on initialize_parameters.
+    void SetPasswordMode();
+
+    /// Sets the text draw type based on initialize_parameters.
+    void SetTextDrawType();
+
+    /// Sets the controller image at the bottom left of the software keyboard.
+    void SetControllerImage();
+
+    /// Disables buttons based on initialize_parameters.
+    void DisableKeyboardButtons();
+
+    /// Changes whether the backspace or/and ok buttons should be enabled or disabled.
+    void SetBackspaceOkEnabled();
+
+    /**
+     * Validates the input text sent in based on the parameters in initialize_parameters.
+     *
+     * @param input_text Input text
+     *
+     * @returns True if the input text is valid, false otherwise.
+     */
+    bool ValidateInputText(const QString& input_text);
+
+    /// Switches between LowerCase and UpperCase (Shift and Caps Lock)
+    void ChangeBottomOSKIndex();
+
+    /// Processes a keyboard button click from the UI as normal keyboard input.
+    void NormalKeyboardButtonClicked(QPushButton* button);
+
+    /// Processes a keyboard button click from the UI as inline keyboard input.
+    void InlineKeyboardButtonClicked(QPushButton* button);
+
+    /**
+     * Inserts a string of arbitrary length into the current_text at the current cursor position.
+     * This is only used for the inline software keyboard.
+     */
+    void InlineTextInsertString(std::u16string_view string);
 
 
-    QDialogButtonBox* buttons;
-    QLabel* header_label;
-    QLabel* sub_label;
-    QLabel* guide_label;
-    QLabel* length_label;
-    QLineEdit* line_edit;
-    QVBoxLayout* layout;
+    /// Setup the mouse hover workaround for "focusing" buttons. This should only be called once.
+    void SetupMouseHover();
 
 
-    Core::Frontend::SoftwareKeyboardParameters parameters;
+    /**
+     * Handles button presses and converts them into keyboard input.
+     *
+     * @tparam HIDButton The list of buttons that can be converted into keyboard input.
+     */
+    template <HIDButton... T>
+    void HandleButtonPressedOnce();
+
+    /**
+     * Handles button holds and converts them into keyboard input.
+     *
+     * @tparam HIDButton The list of buttons that can be converted into keyboard input.
+     */
+    template <HIDButton... T>
+    void HandleButtonHold();
+
+    /**
+     * Translates a button press to focus or click a keyboard button.
+     *
+     * @param button The button press to process.
+     */
+    void TranslateButtonPress(HIDButton button);
+
+    /**
+     * Moves the focus of a button in a certain direction.
+     *
+     * @param direction The direction to move.
+     */
+    void MoveButtonDirection(Direction direction);
+
+    /**
+     * Moves the text cursor in a certain direction.
+     *
+     * @param direction The direction to move.
+     */
+    void MoveTextCursorDirection(Direction direction);
+
+    void StartInputThread();
+    void StopInputThread();
+
+    /// The thread where input is being polled and processed.
+    void InputThread();
+
+    std::unique_ptr<Ui::QtSoftwareKeyboardDialog> ui;
+
+    Core::System& system;
+
+    // True if it is the inline software keyboard.
+    bool is_inline;
+
+    // Common software keyboard initialize parameters.
+    Core::Frontend::KeyboardInitializeParameters initialize_parameters;
+
+    // Used only by the inline software keyboard since the QLineEdit or QTextEdit is hidden.
+    std::u16string current_text;
+    s32 cursor_position{0};
+
+    static constexpr std::size_t NUM_ROWS_NORMAL = 5;
+    static constexpr std::size_t NUM_COLUMNS_NORMAL = 12;
+    static constexpr std::size_t NUM_ROWS_NUMPAD = 4;
+    static constexpr std::size_t NUM_COLUMNS_NUMPAD = 4;
+
+    // Stores the normal keyboard layout.
+    std::array<std::array<std::array<QPushButton*, NUM_COLUMNS_NORMAL>, NUM_ROWS_NORMAL>, 2>
+        keyboard_buttons;
+    // Stores the numberpad keyboard layout.
+    std::array<std::array<QPushButton*, NUM_COLUMNS_NUMPAD>, NUM_ROWS_NUMPAD> numberpad_buttons;
+
+    // Contains a set of all buttons used in keyboard_buttons and numberpad_buttons.
+    std::array<QPushButton*, 110> all_buttons;
+
+    std::size_t row{0};
+    std::size_t column{0};
+
+    BottomOSKIndex bottom_osk_index{BottomOSKIndex::LowerCase};
+    std::atomic<bool> caps_lock_enabled{false};
+
+    std::unique_ptr<InputInterpreter> input_interpreter;
+
+    std::thread input_thread;
+
+    std::atomic<bool> input_thread_running{};
 };
 };
 
 
 class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
 class QtSoftwareKeyboard final : public QObject, public Core::Frontend::SoftwareKeyboardApplet {
@@ -58,19 +232,54 @@ public:
     explicit QtSoftwareKeyboard(GMainWindow& parent);
     explicit QtSoftwareKeyboard(GMainWindow& parent);
     ~QtSoftwareKeyboard() override;
     ~QtSoftwareKeyboard() override;
 
 
-    void RequestText(std::function<void(std::optional<std::u16string>)> out,
-                     Core::Frontend::SoftwareKeyboardParameters parameters) const override;
-    void SendTextCheckDialog(std::u16string error_message,
-                             std::function<void()> finished_check_) const override;
+    void 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_) override;
+
+    void ShowNormalKeyboard() const override;
+
+    void ShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+                             std::u16string text_check_message) const override;
+
+    void ShowInlineKeyboard(
+        Core::Frontend::InlineAppearParameters appear_parameters) const override;
+
+    void HideInlineKeyboard() const override;
+
+    void InlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const override;
+
+    void ExitKeyboard() const override;
 
 
 signals:
 signals:
-    void MainWindowGetText(Core::Frontend::SoftwareKeyboardParameters parameters) const;
-    void MainWindowTextCheckDialog(std::u16string error_message) const;
+    void MainWindowInitializeKeyboard(
+        bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) const;
+
+    void MainWindowShowNormalKeyboard() const;
+
+    void MainWindowShowTextCheckDialog(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+                                       std::u16string text_check_message) const;
+
+    void MainWindowShowInlineKeyboard(
+        Core::Frontend::InlineAppearParameters appear_parameters) const;
+
+    void MainWindowHideInlineKeyboard() const;
+
+    void MainWindowInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters) const;
+
+    void MainWindowExitKeyboard() const;
 
 
 private:
 private:
-    void MainWindowFinishedText(std::optional<std::u16string> text);
-    void MainWindowFinishedCheckDialog();
+    void SubmitNormalText(Service::AM::Applets::SwkbdResult result,
+                          std::u16string submitted_text) const;
+
+    void SubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
+                          std::u16string submitted_text, s32 cursor_position) const;
 
 
-    mutable std::function<void(std::optional<std::u16string>)> text_output;
-    mutable std::function<void()> finished_check;
+    mutable std::function<void(Service::AM::Applets::SwkbdResult, std::u16string)>
+        submit_normal_callback;
+    mutable std::function<void(Service::AM::Applets::SwkbdReplyType, std::u16string, s32)>
+        submit_inline_callback;
 };
 };

+ 3503 - 0
src/yuzu/applets/software_keyboard.ui

@@ -0,0 +1,3503 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtSoftwareKeyboardDialog</class>
+ <widget class="QDialog" name="QtSoftwareKeyboardDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1280</width>
+    <height>720</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Software Keyboard</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true"/>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QWidget" name="mainOSK" native="true">
+     <layout class="QVBoxLayout" name="verticalLayout_2" stretch="320,400">
+      <property name="spacing">
+       <number>0</number>
+      </property>
+      <property name="leftMargin">
+       <number>0</number>
+      </property>
+      <property name="topMargin">
+       <number>0</number>
+      </property>
+      <property name="rightMargin">
+       <number>0</number>
+      </property>
+      <property name="bottomMargin">
+       <number>0</number>
+      </property>
+      <item>
+       <widget class="QStackedWidget" name="topOSK">
+        <property name="currentIndex">
+         <number>0</number>
+        </property>
+        <widget class="QWidget" name="lineOSK">
+         <property name="minimumSize">
+          <size>
+           <width>0</width>
+           <height>100</height>
+          </size>
+         </property>
+         <layout class="QVBoxLayout" name="lineOSKVerticalLayout">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <layout class="QGridLayout" name="gridLineOSK" rowstretch="40,50,23,48,65,94" columnstretch="130,1020,130">
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="spacing">
+             <number>0</number>
+            </property>
+            <item row="4" column="2">
+             <spacer name="horizontalSpacer_3">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="4" column="0">
+             <spacer name="horizontalSpacer_4">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="5" column="1">
+             <widget class="QWidget" name="charactersOSK" native="true">
+              <layout class="QVBoxLayout" name="verticalLayout_5">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>0</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item alignment="Qt::AlignRight|Qt::AlignTop">
+                <widget class="QLabel" name="label_characters">
+                 <property name="font">
+                  <font>
+                   <pointsize>17</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">0/32</string>
+                 </property>
+                 <property name="alignment">
+                  <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <spacer name="verticalSpacer_2">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>40</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="5" column="0">
+             <spacer name="verticalSpacer_3">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>40</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="4" column="1">
+             <widget class="QWidget" name="inputOSK" native="true">
+              <layout class="QVBoxLayout" name="verticalLayout_3">
+               <item>
+                <widget class="QLineEdit" name="line_edit_osk">
+                 <property name="font">
+                  <font>
+                   <pointsize>26</pointsize>
+                   <weight>50</weight>
+                   <bold>false</bold>
+                  </font>
+                 </property>
+                 <property name="focusPolicy">
+                  <enum>Qt::StrongFocus</enum>
+                 </property>
+                 <property name="text">
+                  <string/>
+                 </property>
+                 <property name="maxLength">
+                  <number>32</number>
+                 </property>
+                 <property name="placeholderText">
+                  <string>Enter Text</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="0" column="1">
+             <spacer name="verticalSpacer_4">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>40</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="0" colspan="3">
+             <widget class="QWidget" name="headerOSK" native="true">
+              <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="130,1020,130">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>0</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <spacer name="horizontalSpacer_18">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>127</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_header">
+                 <property name="font">
+                  <font>
+                   <pointsize>23</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string/>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_19">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>127</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="2" column="0" colspan="3">
+             <widget class="QWidget" name="subOSK" native="true">
+              <layout class="QHBoxLayout" name="horizontalLayout_3" stretch="130,1020,130">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>0</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <spacer name="horizontalSpacer_16">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>127</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_sub">
+                 <property name="font">
+                  <font>
+                   <pointsize>17</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string/>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_17">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>127</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="boxOSK">
+         <layout class="QVBoxLayout" name="boxOSKVerticalLayout">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <layout class="QGridLayout" name="gridBoxOSK" rowstretch="61,178,81" columnstretch="120,1040,120">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="topMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="bottomMargin">
+             <number>0</number>
+            </property>
+            <property name="spacing">
+             <number>0</number>
+            </property>
+            <item row="0" column="1">
+             <spacer name="verticalSpacer_5">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>40</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="0">
+             <spacer name="horizontalSpacer_20">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="2">
+             <spacer name="horizontalSpacer_21">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="2" column="1" alignment="Qt::AlignRight|Qt::AlignTop">
+             <widget class="QWidget" name="charactersBoxOSK" native="true">
+              <layout class="QVBoxLayout" name="verticalLayout_4">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>0</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <widget class="QLabel" name="label_characters_box">
+                 <property name="enabled">
+                  <bool>true</bool>
+                 </property>
+                 <property name="font">
+                  <font>
+                   <pointsize>17</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">0/500</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QWidget" name="inputBoxOSK" native="true">
+              <layout class="QVBoxLayout" name="verticalLayout_6">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>14</number>
+               </property>
+               <property name="topMargin">
+                <number>9</number>
+               </property>
+               <property name="rightMargin">
+                <number>14</number>
+               </property>
+               <property name="bottomMargin">
+                <number>9</number>
+               </property>
+               <item>
+                <widget class="QTextEdit" name="text_edit_osk">
+                 <property name="font">
+                  <font>
+                   <pointsize>26</pointsize>
+                  </font>
+                 </property>
+                 <property name="focusPolicy">
+                  <enum>Qt::StrongFocus</enum>
+                 </property>
+                 <property name="html">
+                  <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:26pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+                 </property>
+                </widget>
+               </item>
+              </layout>
+             </widget>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </widget>
+      </item>
+      <item>
+       <widget class="QStackedWidget" name="bottomOSK">
+        <property name="currentIndex">
+         <number>0</number>
+        </property>
+        <widget class="QWidget" name="normalOSK">
+         <layout class="QVBoxLayout" name="normalPageVerticalLayout">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <layout class="QGridLayout" name="kbOSKnormal" rowstretch="15,63,63,63,63,63,70" columnstretch="54,96,96,96,96,96,96,96,96,96,96,96,116,54">
+            <property name="spacing">
+             <number>0</number>
+            </property>
+            <item row="6" column="1" colspan="12">
+             <widget class="QWidget" name="legendOSK" native="true">
+              <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="70,525,25,12,22,41,25,12,22,41,25,12,47,37,29,12,69,37,29,12,56,8">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>2</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <widget class="QWidget" name="icon_controller" native="true">
+                 <property name="styleSheet">
+                  <string notr="true"/>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_5">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_L" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_14">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="arrow_left" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_13">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_R" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_12">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="arrow_right" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_11">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_press_stick" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_10">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_shift">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Shift</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_15">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_X" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_9">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_cancel">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Cancel</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_8">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_A" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_7">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_enter">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Enter</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_6">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="1" column="11">
+             <widget class="QPushButton" name="button_minus">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">-</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="11">
+             <widget class="QPushButton" name="button_apostrophe">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">'</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="11">
+             <widget class="QPushButton" name="button_slash">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">/</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="11">
+             <widget class="QPushButton" name="button_exclamation">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">!</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="7">
+             <widget class="QPushButton" name="button_7">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">7</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="8">
+             <widget class="QPushButton" name="button_8">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">8</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="10">
+             <widget class="QPushButton" name="button_0">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">0</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="9">
+             <widget class="QPushButton" name="button_9">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">9</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="2">
+             <widget class="QPushButton" name="button_w">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">w</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="4">
+             <widget class="QPushButton" name="button_r">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">r</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="3">
+             <widget class="QPushButton" name="button_e">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">e</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QPushButton" name="button_q">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">q</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="7">
+             <widget class="QPushButton" name="button_u">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">u</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="6">
+             <widget class="QPushButton" name="button_y">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">y</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="5">
+             <widget class="QPushButton" name="button_t">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">t</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="9">
+             <widget class="QPushButton" name="button_o">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">o</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="10">
+             <widget class="QPushButton" name="button_p">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">p</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="8">
+             <widget class="QPushButton" name="button_i">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">i</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <widget class="QPushButton" name="button_a">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">a</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="2">
+             <widget class="QPushButton" name="button_s">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">s</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="3">
+             <widget class="QPushButton" name="button_d">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">d</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="4">
+             <widget class="QPushButton" name="button_f">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">f</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="6">
+             <widget class="QPushButton" name="button_h">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">h</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="7">
+             <widget class="QPushButton" name="button_j">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">j</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="5">
+             <widget class="QPushButton" name="button_g">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">g</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="8">
+             <widget class="QPushButton" name="button_k">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">k</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="9">
+             <widget class="QPushButton" name="button_l">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">l</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="10">
+             <widget class="QPushButton" name="button_colon">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">:</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="12" rowspan="2">
+             <widget class="QPushButton" name="button_return">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Return</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="12" rowspan="2">
+             <widget class="QPushButton" name="button_ok">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">OK</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="1">
+             <widget class="QPushButton" name="button_z">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">z</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="3">
+             <widget class="QPushButton" name="button_c">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">c</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="2">
+             <widget class="QPushButton" name="button_x">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">x</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="4">
+             <widget class="QPushButton" name="button_v">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">v</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="7">
+             <widget class="QPushButton" name="button_m">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">m</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="8">
+             <widget class="QPushButton" name="button_comma">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">,</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="6">
+             <widget class="QPushButton" name="button_n">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">n</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="5">
+             <widget class="QPushButton" name="button_b">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">b</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="1" colspan="2">
+             <widget class="QPushButton" name="button_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true"/>
+              </property>
+              <property name="checkable">
+               <bool>true</bool>
+              </property>
+              <property name="checked">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="10">
+             <widget class="QPushButton" name="button_question">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">?</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="9">
+             <widget class="QPushButton" name="button_dot">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">.</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QPushButton" name="button_1">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">1</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="3">
+             <widget class="QPushButton" name="button_3">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">3</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="4">
+             <widget class="QPushButton" name="button_4">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">4</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="2">
+             <widget class="QPushButton" name="button_2">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">2</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="6">
+             <widget class="QPushButton" name="button_6">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">6</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="5">
+             <widget class="QPushButton" name="button_5">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">5</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="3" colspan="9">
+             <widget class="QPushButton" name="button_space">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Space</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="12">
+             <widget class="QPushButton" name="button_backspace">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true"/>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <spacer name="horizontalSpacer">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="0" column="1">
+             <spacer name="verticalSpacer">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="13">
+             <spacer name="horizontalSpacer_2">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="shiftOSK">
+         <layout class="QVBoxLayout" name="shiftPageVerticalLayout">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <layout class="QGridLayout" name="kbOSKshift" rowstretch="15,63,63,63,63,63,70" columnstretch="54,96,96,96,96,96,96,96,96,96,96,96,116,54">
+            <property name="spacing">
+             <number>0</number>
+            </property>
+            <item row="6" column="1" colspan="12">
+             <widget class="QWidget" name="legendOSKshift" native="true">
+              <layout class="QHBoxLayout" name="horizontalLayout_5" stretch="70,464,25,12,22,41,25,12,22,41,25,12,95,37,29,12,69,37,29,12,56,8">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>2</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <widget class="QWidget" name="icon_controller_shift" native="true">
+                 <property name="styleSheet">
+                  <string notr="true"/>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_22">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_L_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_23">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="arrow_left_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_24">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_R_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_25">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="arrow_right_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_26">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_press_stick_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_27">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_shift_shift">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Caps Lock</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_28">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_X_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_29">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_cancel_shift">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Cancel</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_30">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_A_shift" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_31">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_enter_shift">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Enter</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_32">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="1" column="11">
+             <widget class="QPushButton" name="button_underscore">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">_</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="11">
+             <widget class="QPushButton" name="button_quotation">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">&quot;</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="11">
+             <widget class="QPushButton" name="button_at">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">@</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="11">
+             <widget class="QPushButton" name="button_equal">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">=</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="7">
+             <widget class="QPushButton" name="button_ampersand">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">&amp;&amp;</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="8">
+             <widget class="QPushButton" name="button_asterisk">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">*</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="10">
+             <widget class="QPushButton" name="button_right_parenthesis">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">)</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="9">
+             <widget class="QPushButton" name="button_left_parenthesis">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">(</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="2">
+             <widget class="QPushButton" name="button_w_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">W</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="4">
+             <widget class="QPushButton" name="button_r_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">R</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="3">
+             <widget class="QPushButton" name="button_e_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">E</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="1">
+             <widget class="QPushButton" name="button_q_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Q</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="7">
+             <widget class="QPushButton" name="button_u_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">U</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="6">
+             <widget class="QPushButton" name="button_y_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Y</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="5">
+             <widget class="QPushButton" name="button_t_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">T</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="9">
+             <widget class="QPushButton" name="button_o_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">O</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="10">
+             <widget class="QPushButton" name="button_p_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">P</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="8">
+             <widget class="QPushButton" name="button_i_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">I</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="1">
+             <widget class="QPushButton" name="button_a_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">A</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="2">
+             <widget class="QPushButton" name="button_s_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">S</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="3">
+             <widget class="QPushButton" name="button_d_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">D</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="4">
+             <widget class="QPushButton" name="button_f_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">F</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="6">
+             <widget class="QPushButton" name="button_h_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">H</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="7">
+             <widget class="QPushButton" name="button_j_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">J</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="5">
+             <widget class="QPushButton" name="button_g_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">G</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="8">
+             <widget class="QPushButton" name="button_k_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">K</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="9">
+             <widget class="QPushButton" name="button_l_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">L</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="10">
+             <widget class="QPushButton" name="button_semicolon">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">;</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="12" rowspan="2">
+             <widget class="QPushButton" name="button_return_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Return</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="12" rowspan="2">
+             <widget class="QPushButton" name="button_ok_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">OK</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="1">
+             <widget class="QPushButton" name="button_z_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Z</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="3">
+             <widget class="QPushButton" name="button_c_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">C</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="2">
+             <widget class="QPushButton" name="button_x_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">X</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="4">
+             <widget class="QPushButton" name="button_v_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">V</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="7">
+             <widget class="QPushButton" name="button_m_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">M</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="8">
+             <widget class="QPushButton" name="button_less_than">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">&lt;</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="6">
+             <widget class="QPushButton" name="button_n_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">N</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="5">
+             <widget class="QPushButton" name="button_b_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">B</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="1" colspan="2">
+             <widget class="QPushButton" name="button_shift_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true"/>
+              </property>
+              <property name="checkable">
+               <bool>true</bool>
+              </property>
+              <property name="checked">
+               <bool>false</bool>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="10">
+             <widget class="QPushButton" name="button_plus">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">+</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="9">
+             <widget class="QPushButton" name="button_greater_than">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">&gt;</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="1">
+             <widget class="QPushButton" name="button_hash">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">#</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="3">
+             <widget class="QPushButton" name="button_right_bracket">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">]</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="4">
+             <widget class="QPushButton" name="button_dollar">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">$</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="2">
+             <widget class="QPushButton" name="button_left_bracket">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">[</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="6">
+             <widget class="QPushButton" name="button_circumflex">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">^</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="5">
+             <widget class="QPushButton" name="button_percent">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">%</string>
+              </property>
+             </widget>
+            </item>
+            <item row="5" column="3" colspan="9">
+             <widget class="QPushButton" name="button_space_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">Space</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="12">
+             <widget class="QPushButton" name="button_backspace_shift">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true"/>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <spacer name="horizontalSpacer_33">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="0" column="1">
+             <spacer name="verticalSpacer_6">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="13">
+             <spacer name="horizontalSpacer_34">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+        <widget class="QWidget" name="numOSK">
+         <layout class="QVBoxLayout" name="graphicsTabVerticalLayout">
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <layout class="QGridLayout" name="kbOSKnum" rowstretch="18,63,63,63,63,10,70" columnstretch="54,307,186,186,186,120,187,54">
+            <property name="leftMargin">
+             <number>0</number>
+            </property>
+            <property name="rightMargin">
+             <number>0</number>
+            </property>
+            <property name="spacing">
+             <number>0</number>
+            </property>
+            <item row="1" column="5">
+             <widget class="QPushButton" name="button_backspace_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true"/>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="6">
+             <spacer name="horizontalSpacer_35">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="1">
+             <spacer name="horizontalSpacer_36">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="0" column="2">
+             <spacer name="verticalSpacer_7">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="6" column="1" colspan="6">
+             <widget class="QWidget" name="legendOSKnum" native="true">
+              <layout class="QHBoxLayout" name="horizontalLayout_6" stretch="25,70,601,25,12,22,41,25,12,22,41,29,12,69,37,29,12,56,30">
+               <property name="spacing">
+                <number>0</number>
+               </property>
+               <property name="leftMargin">
+                <number>0</number>
+               </property>
+               <property name="topMargin">
+                <number>0</number>
+               </property>
+               <property name="rightMargin">
+                <number>0</number>
+               </property>
+               <property name="bottomMargin">
+                <number>0</number>
+               </property>
+               <item>
+                <spacer name="horizontalSpacer_48">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>40</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="icon_controller_num" native="true">
+                 <property name="styleSheet">
+                  <string notr="true"/>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_38">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_L_num" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_39">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="arrow_left_num" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_40">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_R_num" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_41">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="arrow_right_num" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_42">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_X_num" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_43">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_cancel_num">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Cancel</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_44">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QWidget" name="button_A_num" native="true"/>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_45">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+               <item>
+                <widget class="QLabel" name="label_enter_num">
+                 <property name="font">
+                  <font>
+                   <pointsize>18</pointsize>
+                  </font>
+                 </property>
+                 <property name="text">
+                  <string notr="true">Enter</string>
+                 </property>
+                </widget>
+               </item>
+               <item>
+                <spacer name="horizontalSpacer_46">
+                 <property name="orientation">
+                  <enum>Qt::Horizontal</enum>
+                 </property>
+                 <property name="sizeHint" stdset="0">
+                  <size>
+                   <width>0</width>
+                   <height>20</height>
+                  </size>
+                 </property>
+                </spacer>
+               </item>
+              </layout>
+             </widget>
+            </item>
+            <item row="2" column="4">
+             <widget class="QPushButton" name="button_6_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">6</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="2">
+             <widget class="QPushButton" name="button_4_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">4</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="4">
+             <widget class="QPushButton" name="button_9_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">9</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="3">
+             <widget class="QPushButton" name="button_5_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">5</string>
+              </property>
+             </widget>
+            </item>
+            <item row="2" column="5" rowspan="3">
+             <widget class="QPushButton" name="button_ok_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>18</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">OK</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="2">
+             <widget class="QPushButton" name="button_7_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">7</string>
+              </property>
+             </widget>
+            </item>
+            <item row="3" column="3">
+             <widget class="QPushButton" name="button_8_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">8</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="3">
+             <widget class="QPushButton" name="button_2_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">2</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="2">
+             <widget class="QPushButton" name="button_1_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">1</string>
+              </property>
+             </widget>
+            </item>
+            <item row="4" column="3">
+             <widget class="QPushButton" name="button_0_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">0</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="4">
+             <widget class="QPushButton" name="button_3_num">
+              <property name="sizePolicy">
+               <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                <horstretch>1</horstretch>
+                <verstretch>1</verstretch>
+               </sizepolicy>
+              </property>
+              <property name="font">
+               <font>
+                <pointsize>28</pointsize>
+               </font>
+              </property>
+              <property name="text">
+               <string notr="true">3</string>
+              </property>
+             </widget>
+            </item>
+            <item row="1" column="0">
+             <spacer name="horizontalSpacer_37">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="1" column="7">
+             <spacer name="horizontalSpacer_47">
+              <property name="orientation">
+               <enum>Qt::Horizontal</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>40</width>
+                <height>20</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+            <item row="5" column="3">
+             <spacer name="verticalSpacer_8">
+              <property name="orientation">
+               <enum>Qt::Vertical</enum>
+              </property>
+              <property name="sizeHint" stdset="0">
+               <size>
+                <width>20</width>
+                <height>0</height>
+               </size>
+              </property>
+             </spacer>
+            </item>
+           </layout>
+          </item>
+         </layout>
+        </widget>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>button_1</tabstop>
+  <tabstop>button_2</tabstop>
+  <tabstop>button_3</tabstop>
+  <tabstop>button_4</tabstop>
+  <tabstop>button_5</tabstop>
+  <tabstop>button_6</tabstop>
+  <tabstop>button_7</tabstop>
+  <tabstop>button_8</tabstop>
+  <tabstop>button_9</tabstop>
+  <tabstop>button_0</tabstop>
+  <tabstop>button_minus</tabstop>
+  <tabstop>button_backspace</tabstop>
+  <tabstop>button_q</tabstop>
+  <tabstop>button_w</tabstop>
+  <tabstop>button_e</tabstop>
+  <tabstop>button_r</tabstop>
+  <tabstop>button_t</tabstop>
+  <tabstop>button_y</tabstop>
+  <tabstop>button_u</tabstop>
+  <tabstop>button_i</tabstop>
+  <tabstop>button_o</tabstop>
+  <tabstop>button_p</tabstop>
+  <tabstop>button_slash</tabstop>
+  <tabstop>button_return</tabstop>
+  <tabstop>button_a</tabstop>
+  <tabstop>button_s</tabstop>
+  <tabstop>button_d</tabstop>
+  <tabstop>button_f</tabstop>
+  <tabstop>button_g</tabstop>
+  <tabstop>button_h</tabstop>
+  <tabstop>button_j</tabstop>
+  <tabstop>button_k</tabstop>
+  <tabstop>button_l</tabstop>
+  <tabstop>button_colon</tabstop>
+  <tabstop>button_apostrophe</tabstop>
+  <tabstop>button_z</tabstop>
+  <tabstop>button_x</tabstop>
+  <tabstop>button_c</tabstop>
+  <tabstop>button_v</tabstop>
+  <tabstop>button_b</tabstop>
+  <tabstop>button_n</tabstop>
+  <tabstop>button_m</tabstop>
+  <tabstop>button_comma</tabstop>
+  <tabstop>button_dot</tabstop>
+  <tabstop>button_question</tabstop>
+  <tabstop>button_exclamation</tabstop>
+  <tabstop>button_ok</tabstop>
+  <tabstop>button_shift</tabstop>
+  <tabstop>button_space</tabstop>
+  <tabstop>button_hash</tabstop>
+  <tabstop>button_left_bracket</tabstop>
+  <tabstop>button_right_bracket</tabstop>
+  <tabstop>button_dollar</tabstop>
+  <tabstop>button_percent</tabstop>
+  <tabstop>button_circumflex</tabstop>
+  <tabstop>button_ampersand</tabstop>
+  <tabstop>button_asterisk</tabstop>
+  <tabstop>button_left_parenthesis</tabstop>
+  <tabstop>button_right_parenthesis</tabstop>
+  <tabstop>button_underscore</tabstop>
+  <tabstop>button_backspace_shift</tabstop>
+  <tabstop>button_q_shift</tabstop>
+  <tabstop>button_w_shift</tabstop>
+  <tabstop>button_e_shift</tabstop>
+  <tabstop>button_r_shift</tabstop>
+  <tabstop>button_t_shift</tabstop>
+  <tabstop>button_y_shift</tabstop>
+  <tabstop>button_u_shift</tabstop>
+  <tabstop>button_i_shift</tabstop>
+  <tabstop>button_o_shift</tabstop>
+  <tabstop>button_p_shift</tabstop>
+  <tabstop>button_at</tabstop>
+  <tabstop>button_return_shift</tabstop>
+  <tabstop>button_a_shift</tabstop>
+  <tabstop>button_s_shift</tabstop>
+  <tabstop>button_d_shift</tabstop>
+  <tabstop>button_f_shift</tabstop>
+  <tabstop>button_g_shift</tabstop>
+  <tabstop>button_h_shift</tabstop>
+  <tabstop>button_j_shift</tabstop>
+  <tabstop>button_k_shift</tabstop>
+  <tabstop>button_l_shift</tabstop>
+  <tabstop>button_semicolon</tabstop>
+  <tabstop>button_quotation</tabstop>
+  <tabstop>button_z_shift</tabstop>
+  <tabstop>button_x_shift</tabstop>
+  <tabstop>button_c_shift</tabstop>
+  <tabstop>button_v_shift</tabstop>
+  <tabstop>button_b_shift</tabstop>
+  <tabstop>button_n_shift</tabstop>
+  <tabstop>button_m_shift</tabstop>
+  <tabstop>button_less_than</tabstop>
+  <tabstop>button_greater_than</tabstop>
+  <tabstop>button_plus</tabstop>
+  <tabstop>button_equal</tabstop>
+  <tabstop>button_ok_shift</tabstop>
+  <tabstop>button_shift_shift</tabstop>
+  <tabstop>button_space_shift</tabstop>
+  <tabstop>button_1_num</tabstop>
+  <tabstop>button_2_num</tabstop>
+  <tabstop>button_3_num</tabstop>
+  <tabstop>button_backspace_num</tabstop>
+  <tabstop>button_4_num</tabstop>
+  <tabstop>button_5_num</tabstop>
+  <tabstop>button_6_num</tabstop>
+  <tabstop>button_ok_num</tabstop>
+  <tabstop>button_7_num</tabstop>
+  <tabstop>button_8_num</tabstop>
+  <tabstop>button_9_num</tabstop>
+  <tabstop>button_0_num</tabstop>
+ </tabstops>
+ <resources>
+  <include location="../../../dist/icons/overlay/overlay.qrc"/>
+ </resources>
+ <connections/>
+</ui>

+ 161 - 24
src/yuzu/main.cpp

@@ -101,6 +101,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
 #include "core/perf_stats.h"
 #include "core/perf_stats.h"
 #include "core/telemetry_session.h"
 #include "core/telemetry_session.h"
 #include "input_common/main.h"
 #include "input_common/main.h"
+#include "util/overlay_dialog.h"
 #include "video_core/gpu.h"
 #include "video_core/gpu.h"
 #include "video_core/shader_notify.h"
 #include "video_core/shader_notify.h"
 #include "yuzu/about_dialog.h"
 #include "yuzu/about_dialog.h"
@@ -227,6 +228,8 @@ GMainWindow::GMainWindow()
     SetDiscordEnabled(UISettings::values.enable_discord_presence);
     SetDiscordEnabled(UISettings::values.enable_discord_presence);
     discord_rpc->Update();
     discord_rpc->Update();
 
 
+    RegisterMetaTypes();
+
     InitializeWidgets();
     InitializeWidgets();
     InitializeDebugWidgets();
     InitializeDebugWidgets();
     InitializeRecentFileMenuActions();
     InitializeRecentFileMenuActions();
@@ -375,6 +378,55 @@ GMainWindow::~GMainWindow() {
         delete render_window;
         delete render_window;
 }
 }
 
 
+void GMainWindow::RegisterMetaTypes() {
+    // Register integral and floating point types
+    qRegisterMetaType<u8>("u8");
+    qRegisterMetaType<u16>("u16");
+    qRegisterMetaType<u32>("u32");
+    qRegisterMetaType<u64>("u64");
+    qRegisterMetaType<u128>("u128");
+    qRegisterMetaType<s8>("s8");
+    qRegisterMetaType<s16>("s16");
+    qRegisterMetaType<s32>("s32");
+    qRegisterMetaType<s64>("s64");
+    qRegisterMetaType<f32>("f32");
+    qRegisterMetaType<f64>("f64");
+
+    // Register string types
+    qRegisterMetaType<std::string>("std::string");
+    qRegisterMetaType<std::wstring>("std::wstring");
+    qRegisterMetaType<std::u8string>("std::u8string");
+    qRegisterMetaType<std::u16string>("std::u16string");
+    qRegisterMetaType<std::u32string>("std::u32string");
+    qRegisterMetaType<std::string_view>("std::string_view");
+    qRegisterMetaType<std::wstring_view>("std::wstring_view");
+    qRegisterMetaType<std::u8string_view>("std::u8string_view");
+    qRegisterMetaType<std::u16string_view>("std::u16string_view");
+    qRegisterMetaType<std::u32string_view>("std::u32string_view");
+
+    // Register applet types
+
+    // Controller Applet
+    qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
+
+    // Software Keyboard Applet
+    qRegisterMetaType<Core::Frontend::KeyboardInitializeParameters>(
+        "Core::Frontend::KeyboardInitializeParameters");
+    qRegisterMetaType<Core::Frontend::InlineAppearParameters>(
+        "Core::Frontend::InlineAppearParameters");
+    qRegisterMetaType<Core::Frontend::InlineTextParameters>("Core::Frontend::InlineTextParameters");
+    qRegisterMetaType<Service::AM::Applets::SwkbdResult>("Service::AM::Applets::SwkbdResult");
+    qRegisterMetaType<Service::AM::Applets::SwkbdTextCheckResult>(
+        "Service::AM::Applets::SwkbdTextCheckResult");
+    qRegisterMetaType<Service::AM::Applets::SwkbdReplyType>("Service::AM::Applets::SwkbdReplyType");
+
+    // Web Browser Applet
+    qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason");
+
+    // Register loader types
+    qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
+}
+
 void GMainWindow::ControllerSelectorReconfigureControllers(
 void GMainWindow::ControllerSelectorReconfigureControllers(
     const Core::Frontend::ControllerParameters& parameters) {
     const Core::Frontend::ControllerParameters& parameters) {
     QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
     QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
@@ -414,25 +466,112 @@ void GMainWindow::ProfileSelectorSelectProfile() {
     emit ProfileSelectorFinishedSelection(uuid);
     emit ProfileSelectorFinishedSelection(uuid);
 }
 }
 
 
-void GMainWindow::SoftwareKeyboardGetText(
-    const Core::Frontend::SoftwareKeyboardParameters& parameters) {
-    QtSoftwareKeyboardDialog dialog(this, parameters);
-    dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
-                          Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
-                          Qt::WindowCloseButtonHint);
-    dialog.setWindowModality(Qt::WindowModal);
+void GMainWindow::SoftwareKeyboardInitialize(
+    bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters) {
+    if (software_keyboard) {
+        LOG_ERROR(Frontend, "The software keyboard is already initialized!");
+        return;
+    }
 
 
-    if (dialog.exec() == QDialog::Rejected) {
-        emit SoftwareKeyboardFinishedText(std::nullopt);
+    software_keyboard = new QtSoftwareKeyboardDialog(render_window, Core::System::GetInstance(),
+                                                     is_inline, std::move(initialize_parameters));
+
+    if (is_inline) {
+        connect(
+            software_keyboard, &QtSoftwareKeyboardDialog::SubmitInlineText, this,
+            [this](Service::AM::Applets::SwkbdReplyType reply_type, std::u16string submitted_text,
+                   s32 cursor_position) {
+                emit SoftwareKeyboardSubmitInlineText(reply_type, submitted_text, cursor_position);
+            },
+            Qt::QueuedConnection);
+    } else {
+        connect(
+            software_keyboard, &QtSoftwareKeyboardDialog::SubmitNormalText, this,
+            [this](Service::AM::Applets::SwkbdResult result, std::u16string submitted_text) {
+                emit SoftwareKeyboardSubmitNormalText(result, submitted_text);
+            },
+            Qt::QueuedConnection);
+    }
+}
+
+void GMainWindow::SoftwareKeyboardShowNormal() {
+    if (!software_keyboard) {
+        LOG_ERROR(Frontend, "The software keyboard is not initialized!");
         return;
         return;
     }
     }
 
 
-    emit SoftwareKeyboardFinishedText(dialog.GetText());
+    const auto& layout = render_window->GetFramebufferLayout();
+
+    const auto x = layout.screen.left;
+    const auto y = layout.screen.top;
+    const auto w = layout.screen.GetWidth();
+    const auto h = layout.screen.GetHeight();
+
+    software_keyboard->ShowNormalKeyboard(render_window->mapToGlobal(QPoint(x, y)), QSize(w, h));
 }
 }
 
 
-void GMainWindow::SoftwareKeyboardInvokeCheckDialog(std::u16string error_message) {
-    QMessageBox::warning(this, tr("Text Check Failed"), QString::fromStdU16String(error_message));
-    emit SoftwareKeyboardFinishedCheckDialog();
+void GMainWindow::SoftwareKeyboardShowTextCheck(
+    Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+    std::u16string text_check_message) {
+    if (!software_keyboard) {
+        LOG_ERROR(Frontend, "The software keyboard is not initialized!");
+        return;
+    }
+
+    software_keyboard->ShowTextCheckDialog(text_check_result, text_check_message);
+}
+
+void GMainWindow::SoftwareKeyboardShowInline(
+    Core::Frontend::InlineAppearParameters appear_parameters) {
+    if (!software_keyboard) {
+        LOG_ERROR(Frontend, "The software keyboard is not initialized!");
+        return;
+    }
+
+    const auto& layout = render_window->GetFramebufferLayout();
+
+    const auto x =
+        static_cast<int>(layout.screen.left + (0.5f * layout.screen.GetWidth() *
+                                               ((2.0f * appear_parameters.key_top_translate_x) +
+                                                (1.0f - appear_parameters.key_top_scale_x))));
+    const auto y =
+        static_cast<int>(layout.screen.top + (layout.screen.GetHeight() *
+                                              ((2.0f * appear_parameters.key_top_translate_y) +
+                                               (1.0f - appear_parameters.key_top_scale_y))));
+    const auto w = static_cast<int>(layout.screen.GetWidth() * appear_parameters.key_top_scale_x);
+    const auto h = static_cast<int>(layout.screen.GetHeight() * appear_parameters.key_top_scale_y);
+
+    software_keyboard->ShowInlineKeyboard(std::move(appear_parameters),
+                                          render_window->mapToGlobal(QPoint(x, y)), QSize(w, h));
+}
+
+void GMainWindow::SoftwareKeyboardHideInline() {
+    if (!software_keyboard) {
+        LOG_ERROR(Frontend, "The software keyboard is not initialized!");
+        return;
+    }
+
+    software_keyboard->HideInlineKeyboard();
+}
+
+void GMainWindow::SoftwareKeyboardInlineTextChanged(
+    Core::Frontend::InlineTextParameters text_parameters) {
+    if (!software_keyboard) {
+        LOG_ERROR(Frontend, "The software keyboard is not initialized!");
+        return;
+    }
+
+    software_keyboard->InlineTextChanged(std::move(text_parameters));
+}
+
+void GMainWindow::SoftwareKeyboardExit() {
+    if (!software_keyboard) {
+        return;
+    }
+
+    software_keyboard->ExitKeyboard();
+
+    software_keyboard = nullptr;
 }
 }
 
 
 void GMainWindow::WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args,
 void GMainWindow::WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args,
@@ -978,6 +1117,10 @@ void GMainWindow::ConnectWidgetEvents() {
     connect(this, &GMainWindow::EmulationStopping, render_window,
     connect(this, &GMainWindow::EmulationStopping, render_window,
             &GRenderWindow::OnEmulationStopping);
             &GRenderWindow::OnEmulationStopping);
 
 
+    // Software Keyboard Applet
+    connect(this, &GMainWindow::EmulationStarting, this, &GMainWindow::SoftwareKeyboardExit);
+    connect(this, &GMainWindow::EmulationStopping, this, &GMainWindow::SoftwareKeyboardExit);
+
     connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
     connect(&status_bar_update_timer, &QTimer::timeout, this, &GMainWindow::UpdateStatusBar);
 }
 }
 
 
@@ -2187,15 +2330,6 @@ void GMainWindow::OnStartGame() {
 
 
     emu_thread->SetRunning(true);
     emu_thread->SetRunning(true);
 
 
-    qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
-    qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
-        "Core::Frontend::SoftwareKeyboardParameters");
-    qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
-    qRegisterMetaType<std::string>("std::string");
-    qRegisterMetaType<std::optional<std::u16string>>("std::optional<std::u16string>");
-    qRegisterMetaType<std::string_view>("std::string_view");
-    qRegisterMetaType<Service::AM::Applets::WebExitReason>("Service::AM::Applets::WebExitReason");
-
     connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
     connect(emu_thread.get(), &EmuThread::ErrorThrown, this, &GMainWindow::OnCoreError);
 
 
     ui.action_Start->setEnabled(false);
     ui.action_Start->setEnabled(false);
@@ -2244,8 +2378,11 @@ void GMainWindow::OnExecuteProgram(std::size_t program_index) {
     BootGame(last_filename_booted, program_index);
     BootGame(last_filename_booted, program_index);
 }
 }
 
 
-void GMainWindow::ErrorDisplayDisplayError(QString body) {
-    QMessageBox::critical(this, tr("Error Display"), body);
+void GMainWindow::ErrorDisplayDisplayError(QString error_code, QString error_text) {
+    OverlayDialog dialog(render_window, Core::System::GetInstance(), error_code, error_text,
+                         QString{}, tr("OK"), Qt::AlignLeft | Qt::AlignVCenter);
+    dialog.exec();
+
     emit ErrorDisplayFinished();
     emit ErrorDisplayFinished();
 }
 }
 
 

+ 28 - 7
src/yuzu/main.h

@@ -37,9 +37,13 @@ enum class GameListRemoveTarget;
 enum class InstalledEntryType;
 enum class InstalledEntryType;
 class GameListPlaceholder;
 class GameListPlaceholder;
 
 
+class QtSoftwareKeyboardDialog;
+
 namespace Core::Frontend {
 namespace Core::Frontend {
 struct ControllerParameters;
 struct ControllerParameters;
-struct SoftwareKeyboardParameters;
+struct InlineAppearParameters;
+struct InlineTextParameters;
+struct KeyboardInitializeParameters;
 } // namespace Core::Frontend
 } // namespace Core::Frontend
 
 
 namespace DiscordRPC {
 namespace DiscordRPC {
@@ -57,8 +61,11 @@ class InputSubsystem;
 }
 }
 
 
 namespace Service::AM::Applets {
 namespace Service::AM::Applets {
+enum class SwkbdResult : u32;
+enum class SwkbdTextCheckResult : u32;
+enum class SwkbdReplyType : u32;
 enum class WebExitReason : u32;
 enum class WebExitReason : u32;
-}
+} // namespace Service::AM::Applets
 
 
 enum class EmulatedDirectoryTarget {
 enum class EmulatedDirectoryTarget {
     NAND,
     NAND,
@@ -128,8 +135,10 @@ signals:
 
 
     void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
     void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
 
 
-    void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
-    void SoftwareKeyboardFinishedCheckDialog();
+    void SoftwareKeyboardSubmitNormalText(Service::AM::Applets::SwkbdResult result,
+                                          std::u16string submitted_text);
+    void SoftwareKeyboardSubmitInlineText(Service::AM::Applets::SwkbdReplyType reply_type,
+                                          std::u16string submitted_text, s32 cursor_position);
 
 
     void WebBrowserExtractOfflineRomFS();
     void WebBrowserExtractOfflineRomFS();
     void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url);
     void WebBrowserClosed(Service::AM::Applets::WebExitReason exit_reason, std::string last_url);
@@ -139,15 +148,24 @@ public slots:
     void OnExecuteProgram(std::size_t program_index);
     void OnExecuteProgram(std::size_t program_index);
     void ControllerSelectorReconfigureControllers(
     void ControllerSelectorReconfigureControllers(
         const Core::Frontend::ControllerParameters& parameters);
         const Core::Frontend::ControllerParameters& parameters);
-    void ErrorDisplayDisplayError(QString body);
+    void SoftwareKeyboardInitialize(
+        bool is_inline, Core::Frontend::KeyboardInitializeParameters initialize_parameters);
+    void SoftwareKeyboardShowNormal();
+    void SoftwareKeyboardShowTextCheck(Service::AM::Applets::SwkbdTextCheckResult text_check_result,
+                                       std::u16string text_check_message);
+    void SoftwareKeyboardShowInline(Core::Frontend::InlineAppearParameters appear_parameters);
+    void SoftwareKeyboardHideInline();
+    void SoftwareKeyboardInlineTextChanged(Core::Frontend::InlineTextParameters text_parameters);
+    void SoftwareKeyboardExit();
+    void ErrorDisplayDisplayError(QString error_code, QString error_text);
     void ProfileSelectorSelectProfile();
     void ProfileSelectorSelectProfile();
-    void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
-    void SoftwareKeyboardInvokeCheckDialog(std::u16string error_message);
     void WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args,
     void WebBrowserOpenWebPage(std::string_view main_url, std::string_view additional_args,
                                bool is_local);
                                bool is_local);
     void OnAppFocusStateChanged(Qt::ApplicationState state);
     void OnAppFocusStateChanged(Qt::ApplicationState state);
 
 
 private:
 private:
+    void RegisterMetaTypes();
+
     void InitializeWidgets();
     void InitializeWidgets();
     void InitializeDebugWidgets();
     void InitializeDebugWidgets();
     void InitializeRecentFileMenuActions();
     void InitializeRecentFileMenuActions();
@@ -334,6 +352,9 @@ private:
     // Disables the web applet for the rest of the emulated session
     // Disables the web applet for the rest of the emulated session
     bool disable_web_applet{};
     bool disable_web_applet{};
 
 
+    // Applets
+    QtSoftwareKeyboardDialog* software_keyboard = nullptr;
+
 protected:
 protected:
     void dropEvent(QDropEvent* event) override;
     void dropEvent(QDropEvent* event) override;
     void dragEnterEvent(QDragEnterEvent* event) override;
     void dragEnterEvent(QDragEnterEvent* event) override;

+ 249 - 0
src/yuzu/util/overlay_dialog.cpp

@@ -0,0 +1,249 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QKeyEvent>
+#include <QScreen>
+
+#include "core/core.h"
+#include "core/frontend/input_interpreter.h"
+#include "ui_overlay_dialog.h"
+#include "yuzu/util/overlay_dialog.h"
+
+namespace {
+
+constexpr float BASE_TITLE_FONT_SIZE = 14.0f;
+constexpr float BASE_FONT_SIZE = 18.0f;
+constexpr float BASE_WIDTH = 1280.0f;
+constexpr float BASE_HEIGHT = 720.0f;
+
+} // Anonymous namespace
+
+OverlayDialog::OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text,
+                             const QString& body_text, const QString& left_button_text,
+                             const QString& right_button_text, Qt::Alignment alignment,
+                             bool use_rich_text_)
+    : QDialog(parent), ui{std::make_unique<Ui::OverlayDialog>()}, use_rich_text{use_rich_text_} {
+    ui->setupUi(this);
+
+    setWindowFlags(Qt::Dialog | Qt::FramelessWindowHint | Qt::WindowTitleHint |
+                   Qt::WindowSystemMenuHint | Qt::CustomizeWindowHint);
+    setWindowModality(Qt::WindowModal);
+    setAttribute(Qt::WA_TranslucentBackground);
+
+    if (use_rich_text) {
+        InitializeRichTextDialog(title_text, body_text, left_button_text, right_button_text,
+                                 alignment);
+    } else {
+        InitializeRegularTextDialog(title_text, body_text, left_button_text, right_button_text,
+                                    alignment);
+    }
+
+    MoveAndResizeWindow();
+
+    // TODO (Morph): Remove this when InputInterpreter no longer relies on the HID backend
+    if (system.IsPoweredOn()) {
+        input_interpreter = std::make_unique<InputInterpreter>(system);
+
+        StartInputThread();
+    }
+}
+
+OverlayDialog::~OverlayDialog() {
+    StopInputThread();
+}
+
+void OverlayDialog::InitializeRegularTextDialog(const QString& title_text, const QString& body_text,
+                                                const QString& left_button_text,
+                                                const QString& right_button_text,
+                                                Qt::Alignment alignment) {
+    ui->stackedDialog->setCurrentIndex(0);
+
+    ui->label_title->setText(title_text);
+    ui->label_dialog->setText(body_text);
+    ui->button_cancel->setText(left_button_text);
+    ui->button_ok_label->setText(right_button_text);
+
+    ui->label_dialog->setAlignment(alignment);
+
+    if (title_text.isEmpty()) {
+        ui->label_title->hide();
+        ui->verticalLayout_2->setStretch(0, 0);
+        ui->verticalLayout_2->setStretch(1, 219);
+        ui->verticalLayout_2->setStretch(2, 82);
+    }
+
+    if (left_button_text.isEmpty()) {
+        ui->button_cancel->hide();
+        ui->button_cancel->setEnabled(false);
+    }
+
+    if (right_button_text.isEmpty()) {
+        ui->button_ok_label->hide();
+        ui->button_ok_label->setEnabled(false);
+    }
+
+    connect(
+        ui->button_cancel, &QPushButton::clicked, this,
+        [this](bool) {
+            StopInputThread();
+            QDialog::reject();
+        },
+        Qt::QueuedConnection);
+    connect(
+        ui->button_ok_label, &QPushButton::clicked, this,
+        [this](bool) {
+            StopInputThread();
+            QDialog::accept();
+        },
+        Qt::QueuedConnection);
+}
+
+void OverlayDialog::InitializeRichTextDialog(const QString& title_text, const QString& body_text,
+                                             const QString& left_button_text,
+                                             const QString& right_button_text,
+                                             Qt::Alignment alignment) {
+    ui->stackedDialog->setCurrentIndex(1);
+
+    ui->label_title_rich->setText(title_text);
+    ui->text_browser_dialog->setText(body_text);
+    ui->button_cancel_rich->setText(left_button_text);
+    ui->button_ok_rich->setText(right_button_text);
+
+    // TODO (Morph/Rei): Replace this with something that works better
+    ui->text_browser_dialog->setAlignment(alignment);
+
+    if (title_text.isEmpty()) {
+        ui->label_title_rich->hide();
+        ui->verticalLayout_3->setStretch(0, 0);
+        ui->verticalLayout_3->setStretch(1, 438);
+        ui->verticalLayout_3->setStretch(2, 82);
+    }
+
+    if (left_button_text.isEmpty()) {
+        ui->button_cancel_rich->hide();
+        ui->button_cancel_rich->setEnabled(false);
+    }
+
+    if (right_button_text.isEmpty()) {
+        ui->button_ok_rich->hide();
+        ui->button_ok_rich->setEnabled(false);
+    }
+
+    connect(
+        ui->button_cancel_rich, &QPushButton::clicked, this,
+        [this](bool) {
+            StopInputThread();
+            QDialog::reject();
+        },
+        Qt::QueuedConnection);
+    connect(
+        ui->button_ok_rich, &QPushButton::clicked, this,
+        [this](bool) {
+            StopInputThread();
+            QDialog::accept();
+        },
+        Qt::QueuedConnection);
+}
+
+void OverlayDialog::MoveAndResizeWindow() {
+    const auto pos = parentWidget()->mapToGlobal(parentWidget()->rect().topLeft());
+    const auto width = static_cast<float>(parentWidget()->width());
+    const auto height = static_cast<float>(parentWidget()->height());
+
+    // High DPI
+    const float dpi_scale = qApp->screenAt(pos)->logicalDotsPerInch() / 96.0f;
+
+    const auto title_text_font_size = BASE_TITLE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
+    const auto body_text_font_size =
+        BASE_FONT_SIZE * (((width / BASE_WIDTH) + (height / BASE_HEIGHT)) / 2.0f) / dpi_scale;
+    const auto button_text_font_size = BASE_FONT_SIZE * (height / BASE_HEIGHT) / dpi_scale;
+
+    QFont title_text_font(QStringLiteral("MS Shell Dlg 2"), title_text_font_size, QFont::Normal);
+    QFont body_text_font(QStringLiteral("MS Shell Dlg 2"), body_text_font_size, QFont::Normal);
+    QFont button_text_font(QStringLiteral("MS Shell Dlg 2"), button_text_font_size, QFont::Normal);
+
+    if (use_rich_text) {
+        ui->label_title_rich->setFont(title_text_font);
+        ui->text_browser_dialog->setFont(body_text_font);
+        ui->button_cancel_rich->setFont(button_text_font);
+        ui->button_ok_rich->setFont(button_text_font);
+    } else {
+        ui->label_title->setFont(title_text_font);
+        ui->label_dialog->setFont(body_text_font);
+        ui->button_cancel->setFont(button_text_font);
+        ui->button_ok_label->setFont(button_text_font);
+    }
+
+    QDialog::move(pos);
+    QDialog::resize(width, height);
+}
+
+template <HIDButton... T>
+void OverlayDialog::HandleButtonPressedOnce() {
+    const auto f = [this](HIDButton button) {
+        if (input_interpreter->IsButtonPressedOnce(button)) {
+            TranslateButtonPress(button);
+        }
+    };
+
+    (f(T), ...);
+}
+
+void OverlayDialog::TranslateButtonPress(HIDButton button) {
+    QPushButton* left_button = use_rich_text ? ui->button_cancel_rich : ui->button_cancel;
+    QPushButton* right_button = use_rich_text ? ui->button_ok_rich : ui->button_ok_label;
+
+    // TODO (Morph): Handle QTextBrowser text scrolling
+    // TODO (Morph): focusPrevious/NextChild() doesn't work well with the rich text dialog, fix it
+
+    switch (button) {
+    case HIDButton::A:
+    case HIDButton::B:
+        if (left_button->hasFocus()) {
+            left_button->click();
+        } else if (right_button->hasFocus()) {
+            right_button->click();
+        }
+        break;
+    case HIDButton::DLeft:
+    case HIDButton::LStickLeft:
+        focusPreviousChild();
+        break;
+    case HIDButton::DRight:
+    case HIDButton::LStickRight:
+        focusNextChild();
+        break;
+    default:
+        break;
+    }
+}
+
+void OverlayDialog::StartInputThread() {
+    if (input_thread_running) {
+        return;
+    }
+
+    input_thread_running = true;
+
+    input_thread = std::thread(&OverlayDialog::InputThread, this);
+}
+
+void OverlayDialog::StopInputThread() {
+    input_thread_running = false;
+
+    if (input_thread.joinable()) {
+        input_thread.join();
+    }
+}
+
+void OverlayDialog::InputThread() {
+    while (input_thread_running) {
+        input_interpreter->PollInput();
+
+        HandleButtonPressedOnce<HIDButton::A, HIDButton::B, HIDButton::DLeft, HIDButton::DRight,
+                                HIDButton::LStickLeft, HIDButton::LStickRight>();
+
+        std::this_thread::sleep_for(std::chrono::milliseconds(50));
+    }
+}

+ 107 - 0
src/yuzu/util/overlay_dialog.h

@@ -0,0 +1,107 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <atomic>
+#include <memory>
+#include <thread>
+
+#include <QDialog>
+
+#include "common/common_types.h"
+
+enum class HIDButton : u8;
+
+class InputInterpreter;
+
+namespace Core {
+class System;
+}
+
+namespace Ui {
+class OverlayDialog;
+}
+
+/**
+ * An OverlayDialog is an interactive dialog that accepts controller input (while a game is running)
+ * This dialog attempts to replicate the look and feel of the Nintendo Switch's overlay dialogs and
+ * provide some extra features such as embedding HTML/Rich Text content in a QTextBrowser.
+ * The OverlayDialog provides 2 modes: one to embed regular text into a QLabel and another to embed
+ * HTML/Rich Text content into a QTextBrowser.
+ */
+class OverlayDialog final : public QDialog {
+    Q_OBJECT
+
+public:
+    explicit OverlayDialog(QWidget* parent, Core::System& system, const QString& title_text,
+                           const QString& body_text, const QString& left_button_text,
+                           const QString& right_button_text,
+                           Qt::Alignment alignment = Qt::AlignCenter, bool use_rich_text_ = false);
+    ~OverlayDialog() override;
+
+private:
+    /**
+     * Initializes a text dialog with a QLabel storing text.
+     * Only use this for short text as the dialog buttons would be squashed with longer text.
+     *
+     * @param title_text Title text to be displayed
+     * @param body_text Main text to be displayed
+     * @param left_button_text Left button text. If empty, the button is hidden and disabled
+     * @param right_button_text Right button text. If empty, the button is hidden and disabled
+     * @param alignment Main text alignment
+     */
+    void InitializeRegularTextDialog(const QString& title_text, const QString& body_text,
+                                     const QString& left_button_text,
+                                     const QString& right_button_text, Qt::Alignment alignment);
+
+    /**
+     * Initializes a text dialog with a QTextBrowser storing text.
+     * This is ideal for longer text or rich text content. A scrollbar is shown for longer text.
+     *
+     * @param title_text Title text to be displayed
+     * @param body_text Main text to be displayed
+     * @param left_button_text Left button text. If empty, the button is hidden and disabled
+     * @param right_button_text Right button text. If empty, the button is hidden and disabled
+     * @param alignment Main text alignment
+     */
+    void InitializeRichTextDialog(const QString& title_text, const QString& body_text,
+                                  const QString& left_button_text, const QString& right_button_text,
+                                  Qt::Alignment alignment);
+
+    /// Moves and resizes the dialog to be fully overlayed on top of the parent window.
+    void MoveAndResizeWindow();
+
+    /**
+     * Handles button presses and converts them into keyboard input.
+     *
+     * @tparam HIDButton The list of buttons that can be converted into keyboard input.
+     */
+    template <HIDButton... T>
+    void HandleButtonPressedOnce();
+
+    /**
+     * Translates a button press to focus or click either the left or right buttons.
+     *
+     * @param button The button press to process.
+     */
+    void TranslateButtonPress(HIDButton button);
+
+    void StartInputThread();
+    void StopInputThread();
+
+    /// The thread where input is being polled and processed.
+    void InputThread();
+
+    std::unique_ptr<Ui::OverlayDialog> ui;
+
+    bool use_rich_text;
+
+    std::unique_ptr<InputInterpreter> input_interpreter;
+
+    std::thread input_thread;
+
+    std::atomic<bool> input_thread_running{};
+};

+ 404 - 0
src/yuzu/util/overlay_dialog.ui

@@ -0,0 +1,404 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OverlayDialog</class>
+ <widget class="QDialog" name="OverlayDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>1280</width>
+    <height>720</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Dialog</string>
+  </property>
+  <property name="styleSheet">
+   <string notr="true"/>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="spacing">
+    <number>0</number>
+   </property>
+   <property name="leftMargin">
+    <number>0</number>
+   </property>
+   <property name="topMargin">
+    <number>0</number>
+   </property>
+   <property name="rightMargin">
+    <number>0</number>
+   </property>
+   <property name="bottomMargin">
+    <number>0</number>
+   </property>
+   <item>
+    <widget class="QStackedWidget" name="stackedDialog">
+     <property name="currentIndex">
+      <number>0</number>
+     </property>
+     <widget class="QWidget" name="lineDialog">
+      <layout class="QGridLayout" name="lineDialogGridLayout" rowstretch="210,300,210" columnstretch="250,780,250">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item row="1" column="1">
+        <widget class="QWidget" name="contentDialog" native="true">
+         <layout class="QVBoxLayout" name="verticalLayout_2" stretch="70,149,82">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="label_title">
+            <property name="font">
+             <font>
+              <pointsize>14</pointsize>
+             </font>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QLabel" name="label_dialog">
+            <property name="font">
+             <font>
+              <pointsize>18</pointsize>
+             </font>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignCenter</set>
+            </property>
+            <property name="wordWrap">
+             <bool>true</bool>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QWidget" name="buttonsDialog" native="true">
+            <layout class="QHBoxLayout" name="horizontalLayout">
+             <property name="spacing">
+              <number>0</number>
+             </property>
+             <property name="leftMargin">
+              <number>0</number>
+             </property>
+             <property name="topMargin">
+              <number>0</number>
+             </property>
+             <property name="rightMargin">
+              <number>0</number>
+             </property>
+             <property name="bottomMargin">
+              <number>0</number>
+             </property>
+             <item>
+              <widget class="QPushButton" name="button_cancel">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="font">
+                <font>
+                 <pointsize>18</pointsize>
+                </font>
+               </property>
+               <property name="text">
+                <string>Cancel</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="button_ok_label">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="font">
+                <font>
+                 <pointsize>18</pointsize>
+                </font>
+               </property>
+               <property name="text">
+                <string>OK</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="0" column="1">
+        <spacer name="verticalSpacer">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="0">
+        <spacer name="horizontalSpacer">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="2" column="1">
+        <spacer name="verticalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="2">
+        <spacer name="horizontalSpacer_2">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+     <widget class="QWidget" name="richDialog">
+      <layout class="QGridLayout" name="richDialogGridLayout" rowstretch="100,520,100" columnstretch="165,950,165">
+       <property name="leftMargin">
+        <number>0</number>
+       </property>
+       <property name="topMargin">
+        <number>0</number>
+       </property>
+       <property name="rightMargin">
+        <number>0</number>
+       </property>
+       <property name="bottomMargin">
+        <number>0</number>
+       </property>
+       <property name="spacing">
+        <number>0</number>
+       </property>
+       <item row="1" column="0">
+        <spacer name="horizontalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="2" column="1">
+        <spacer name="verticalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="0" column="1">
+        <spacer name="verticalSpacer_3">
+         <property name="orientation">
+          <enum>Qt::Vertical</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>20</width>
+           <height>40</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+       <item row="1" column="1">
+        <widget class="QWidget" name="contentRichDialog" native="true">
+         <layout class="QVBoxLayout" name="verticalLayout_3" stretch="70,368,82">
+          <property name="spacing">
+           <number>0</number>
+          </property>
+          <property name="leftMargin">
+           <number>0</number>
+          </property>
+          <property name="topMargin">
+           <number>0</number>
+          </property>
+          <property name="rightMargin">
+           <number>0</number>
+          </property>
+          <property name="bottomMargin">
+           <number>0</number>
+          </property>
+          <item>
+           <widget class="QLabel" name="label_title_rich">
+            <property name="font">
+             <font>
+              <pointsize>14</pointsize>
+             </font>
+            </property>
+            <property name="text">
+             <string/>
+            </property>
+            <property name="alignment">
+             <set>Qt::AlignBottom|Qt::AlignLeading|Qt::AlignLeft</set>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QTextBrowser" name="text_browser_dialog">
+            <property name="font">
+             <font>
+              <pointsize>18</pointsize>
+             </font>
+            </property>
+            <property name="html">
+             <string>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QWidget" name="buttonsRichDialog" native="true">
+            <layout class="QHBoxLayout" name="horizontalLayout_2">
+             <property name="spacing">
+              <number>0</number>
+             </property>
+             <property name="leftMargin">
+              <number>0</number>
+             </property>
+             <property name="topMargin">
+              <number>0</number>
+             </property>
+             <property name="rightMargin">
+              <number>0</number>
+             </property>
+             <property name="bottomMargin">
+              <number>0</number>
+             </property>
+             <item>
+              <widget class="QPushButton" name="button_cancel_rich">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="font">
+                <font>
+                 <pointsize>18</pointsize>
+                </font>
+               </property>
+               <property name="text">
+                <string>Cancel</string>
+               </property>
+              </widget>
+             </item>
+             <item>
+              <widget class="QPushButton" name="button_ok_rich">
+               <property name="sizePolicy">
+                <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+                 <horstretch>0</horstretch>
+                 <verstretch>0</verstretch>
+                </sizepolicy>
+               </property>
+               <property name="font">
+                <font>
+                 <pointsize>18</pointsize>
+                </font>
+               </property>
+               <property name="text">
+                <string>OK</string>
+               </property>
+              </widget>
+             </item>
+            </layout>
+           </widget>
+          </item>
+         </layout>
+        </widget>
+       </item>
+       <item row="1" column="2">
+        <spacer name="horizontalSpacer_4">
+         <property name="orientation">
+          <enum>Qt::Horizontal</enum>
+         </property>
+         <property name="sizeHint" stdset="0">
+          <size>
+           <width>40</width>
+           <height>20</height>
+          </size>
+         </property>
+        </spacer>
+       </item>
+      </layout>
+     </widget>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <resources>
+  <include location="../../../dist/icons/overlay/overlay.qrc"/>
+ </resources>
+ <connections/>
+</ui>