Merge pull request #6133 from Morph1984/project-eleuthia
Project Eleuthia - On-Screen Keyboard and Error Applet Overlaysmerge-requests/60/head
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 712 B |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 683 B |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.1 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 796 B |
After Width: | Height: | Size: 745 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 3.9 KiB |
After Width: | Height: | Size: 3.4 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 5.1 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 4.5 KiB |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 9.3 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 7.3 KiB |
After Width: | Height: | Size: 6.6 KiB |
After Width: | Height: | Size: 741 B |
After Width: | Height: | Size: 767 B |
After Width: | Height: | Size: 781 B |
After Width: | Height: | Size: 791 B |
After Width: | Height: | Size: 726 B |
After Width: | Height: | Size: 502 B |
After Width: | Height: | Size: 694 B |
After Width: | Height: | Size: 699 B |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 626 B |
After Width: | Height: | Size: 676 B |
After Width: | Height: | Size: 645 B |
After Width: | Height: | Size: 664 B |
After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 281 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 1.9 KiB |
@ -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>
|
@ -1,29 +1,149 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// 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 "core/frontend/applets/software_keyboard.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
|
||||
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;
|
||||
|
||||
out(parameters.initial_text);
|
||||
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::SendTextCheckDialog(
|
||||
std::u16string error_message, std::function<void()> finished_check) const {
|
||||
void DefaultSoftwareKeyboardApplet::ShowNormalKeyboard() const {
|
||||
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 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();
|
||||
}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::HideInlineKeyboard() const {
|
||||
LOG_WARNING(Service_AM,
|
||||
"(STUBBED) called, backend requested to hide the inline software keyboard.");
|
||||
}
|
||||
|
||||
void DefaultSoftwareKeyboardApplet::InlineTextChanged(InlineTextParameters text_parameters) const {
|
||||
LOG_WARNING(Service_AM,
|
||||
"(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
|
||||
|
@ -1,54 +1,116 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Copyright 2021 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include "common/bit_field.h"
|
||||
#include <thread>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "core/hle/service/am/applets/software_keyboard_types.h"
|
||||
|
||||
namespace Core::Frontend {
|
||||
struct SoftwareKeyboardParameters {
|
||||
std::u16string submit_text;
|
||||
|
||||
struct KeyboardInitializeParameters {
|
||||
std::u16string ok_text;
|
||||
std::u16string header_text;
|
||||
std::u16string sub_text;
|
||||
std::u16string guide_text;
|
||||
std::u16string initial_text;
|
||||
std::size_t max_length;
|
||||
bool password;
|
||||
bool cursor_at_beginning;
|
||||
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;
|
||||
};
|
||||
|
||||
union {
|
||||
u8 value;
|
||||
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;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
struct InlineTextParameters {
|
||||
std::u16string input_text;
|
||||
s32 cursor_position;
|
||||
};
|
||||
|
||||
class SoftwareKeyboardApplet {
|
||||
public:
|
||||
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 {
|
||||
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
|
||||
|
@ -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
|
@ -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));
|
||||
}
|
||||
}
|
@ -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{};
|
||||
};
|
@ -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><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
||||
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||
p, li { white-space: pre-wrap; }
|
||||
</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:18pt; font-weight:400; font-style:normal;">
|
||||
<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html></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>
|