diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/core/hle/service/acc/profile_manager.h | 3 | ||||
-rw-r--r-- | src/core/hle/service/audio/hwopus.cpp | 2 | ||||
-rw-r--r-- | src/core/hle/service/nvflinger/buffer_queue.h | 4 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_rasterizer.cpp | 4 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.cpp | 246 | ||||
-rw-r--r-- | src/video_core/renderer_opengl/gl_state.h | 13 | ||||
-rw-r--r-- | src/yuzu/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/yuzu/configuration/configure_system.cpp | 43 | ||||
-rw-r--r-- | src/yuzu/main.cpp | 2 | ||||
-rw-r--r-- | src/yuzu/util/limitable_input_dialog.cpp | 59 | ||||
-rw-r--r-- | src/yuzu/util/limitable_input_dialog.h | 31 |
11 files changed, 275 insertions, 134 deletions
diff --git a/src/core/hle/service/acc/profile_manager.h b/src/core/hle/service/acc/profile_manager.h index 1cd2e51b2..747c46c20 100644 --- a/src/core/hle/service/acc/profile_manager.h +++ b/src/core/hle/service/acc/profile_manager.h @@ -57,7 +57,8 @@ struct UUID { }; static_assert(sizeof(UUID) == 16, "UUID is an invalid size!"); -using ProfileUsername = std::array<u8, 0x20>; +constexpr std::size_t profile_username_size = 32; +using ProfileUsername = std::array<u8, profile_username_size>; using ProfileData = std::array<u8, MAX_DATA>; using UserIDArray = std::array<UUID, MAX_USERS>; diff --git a/src/core/hle/service/audio/hwopus.cpp b/src/core/hle/service/audio/hwopus.cpp index 7168c6a10..783c39503 100644 --- a/src/core/hle/service/audio/hwopus.cpp +++ b/src/core/hle/service/audio/hwopus.cpp @@ -161,7 +161,7 @@ void HwOpus::OpenOpusDecoder(Kernel::HLERequestContext& ctx) { ASSERT_MSG(channel_count == 1 || channel_count == 2, "Invalid channel count"); std::size_t worker_sz = WorkerBufferSize(channel_count); - ASSERT_MSG(buffer_sz < worker_sz, "Worker buffer too large"); + ASSERT_MSG(buffer_sz >= worker_sz, "Worker buffer too large"); std::unique_ptr<OpusDecoder, OpusDeleter> decoder{ static_cast<OpusDecoder*>(operator new(worker_sz))}; if (opus_decoder_init(decoder.get(), sample_rate, channel_count)) { diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h index 2fe81a560..8cff5eb71 100644 --- a/src/core/hle/service/nvflinger/buffer_queue.h +++ b/src/core/hle/service/nvflinger/buffer_queue.h @@ -58,9 +58,9 @@ public: /// Rotate source image 90 degrees clockwise Rotate90 = 0x04, /// Rotate source image 180 degrees - Roate180 = 0x03, + Rotate180 = 0x03, /// Rotate source image 270 degrees clockwise - Roate270 = 0x07, + Rotate270 = 0x07, }; struct Buffer { diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 75e31c6de..a0527fe57 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -104,7 +104,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo } ASSERT_MSG(has_ARB_separate_shader_objects, "has_ARB_separate_shader_objects is unsupported"); - + OpenGLState::ApplyDefaultState(); // Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0 state.clip_distance[0] = true; @@ -115,8 +115,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window, ScreenInfo state.draw.shader_program = 0; state.Apply(); - glEnable(GL_BLEND); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index d8a43cc94..b6b426f34 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -89,7 +89,18 @@ OpenGLState::OpenGLState() { point.size = 1; } -void OpenGLState::Apply() const { +void OpenGLState::ApplyDefaultState() { + glDisable(GL_FRAMEBUFFER_SRGB); + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_PRIMITIVE_RESTART); + glDisable(GL_STENCIL_TEST); + glEnable(GL_BLEND); + glDisable(GL_COLOR_LOGIC_OP); + glDisable(GL_SCISSOR_TEST); +} + +void OpenGLState::ApplySRgb() const { // sRGB if (framebuffer_srgb.enabled != cur_state.framebuffer_srgb.enabled) { if (framebuffer_srgb.enabled) { @@ -100,96 +111,122 @@ void OpenGLState::Apply() const { glDisable(GL_FRAMEBUFFER_SRGB); } } +} + +void OpenGLState::ApplyCulling() const { // Culling - if (cull.enabled != cur_state.cull.enabled) { + const bool cull_changed = cull.enabled != cur_state.cull.enabled; + if (cull_changed) { if (cull.enabled) { glEnable(GL_CULL_FACE); } else { glDisable(GL_CULL_FACE); } } + if (cull.enabled) { + if (cull_changed || cull.mode != cur_state.cull.mode) { + glCullFace(cull.mode); + } - if (cull.mode != cur_state.cull.mode) { - glCullFace(cull.mode); - } - - if (cull.front_face != cur_state.cull.front_face) { - glFrontFace(cull.front_face); + if (cull_changed || cull.front_face != cur_state.cull.front_face) { + glFrontFace(cull.front_face); + } } +} +void OpenGLState::ApplyDepth() const { // Depth test - if (depth.test_enabled != cur_state.depth.test_enabled) { + const bool depth_test_changed = depth.test_enabled != cur_state.depth.test_enabled; + if (depth_test_changed) { if (depth.test_enabled) { glEnable(GL_DEPTH_TEST); } else { glDisable(GL_DEPTH_TEST); } } - - if (depth.test_func != cur_state.depth.test_func) { + if (depth.test_enabled && + (depth_test_changed || depth.test_func != cur_state.depth.test_func)) { glDepthFunc(depth.test_func); } - // Depth mask if (depth.write_mask != cur_state.depth.write_mask) { glDepthMask(depth.write_mask); } - // Depth range if (depth.depth_range_near != cur_state.depth.depth_range_near || depth.depth_range_far != cur_state.depth.depth_range_far) { glDepthRange(depth.depth_range_near, depth.depth_range_far); } +} - // Primitive restart - if (primitive_restart.enabled != cur_state.primitive_restart.enabled) { +void OpenGLState::ApplyPrimitiveRestart() const { + const bool primitive_restart_changed = + primitive_restart.enabled != cur_state.primitive_restart.enabled; + if (primitive_restart_changed) { if (primitive_restart.enabled) { glEnable(GL_PRIMITIVE_RESTART); } else { glDisable(GL_PRIMITIVE_RESTART); } } - if (primitive_restart.index != cur_state.primitive_restart.index) { + if (primitive_restart_changed || + (primitive_restart.enabled && + primitive_restart.index != cur_state.primitive_restart.index)) { glPrimitiveRestartIndex(primitive_restart.index); } +} - // Color mask - if (color_mask.red_enabled != cur_state.color_mask.red_enabled || - color_mask.green_enabled != cur_state.color_mask.green_enabled || - color_mask.blue_enabled != cur_state.color_mask.blue_enabled || - color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) { - glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled, - color_mask.alpha_enabled); - } - - // Stencil test - if (stencil.test_enabled != cur_state.stencil.test_enabled) { +void OpenGLState::ApplyStencilTest() const { + const bool stencil_test_changed = stencil.test_enabled != cur_state.stencil.test_enabled; + if (stencil_test_changed) { if (stencil.test_enabled) { glEnable(GL_STENCIL_TEST); } else { glDisable(GL_STENCIL_TEST); } } - auto config_stencil = [](GLenum face, const auto& config, const auto& prev_config) { - if (config.test_func != prev_config.test_func || config.test_ref != prev_config.test_ref || - config.test_mask != prev_config.test_mask) { - glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); - } - if (config.action_depth_fail != prev_config.action_depth_fail || - config.action_depth_pass != prev_config.action_depth_pass || - config.action_stencil_fail != prev_config.action_stencil_fail) { - glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, - config.action_depth_pass); - } - if (config.write_mask != prev_config.write_mask) { - glStencilMaskSeparate(face, config.write_mask); + if (stencil.test_enabled) { + auto config_stencil = [stencil_test_changed](GLenum face, const auto& config, + const auto& prev_config) { + if (stencil_test_changed || config.test_func != prev_config.test_func || + config.test_ref != prev_config.test_ref || + config.test_mask != prev_config.test_mask) { + glStencilFuncSeparate(face, config.test_func, config.test_ref, config.test_mask); + } + if (stencil_test_changed || config.action_depth_fail != prev_config.action_depth_fail || + config.action_depth_pass != prev_config.action_depth_pass || + config.action_stencil_fail != prev_config.action_stencil_fail) { + glStencilOpSeparate(face, config.action_stencil_fail, config.action_depth_fail, + config.action_depth_pass); + } + if (config.write_mask != prev_config.write_mask) { + glStencilMaskSeparate(face, config.write_mask); + } + }; + config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); + config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); + } +} + +void OpenGLState::ApplyScissorTest() const { + const bool scissor_changed = scissor.enabled != cur_state.scissor.enabled; + if (scissor_changed) { + if (scissor.enabled) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); } - }; - config_stencil(GL_FRONT, stencil.front, cur_state.stencil.front); - config_stencil(GL_BACK, stencil.back, cur_state.stencil.back); + } + if (scissor_changed || scissor_changed || scissor.x != cur_state.scissor.x || + scissor.y != cur_state.scissor.y || scissor.width != cur_state.scissor.width || + scissor.height != cur_state.scissor.height) { + glScissor(scissor.x, scissor.y, scissor.width, scissor.height); + } +} - // Blending - if (blend.enabled != cur_state.blend.enabled) { +void OpenGLState::ApplyBlending() const { + const bool blend_changed = blend.enabled != cur_state.blend.enabled; + if (blend_changed) { if (blend.enabled) { ASSERT(!logic_op.enabled); glEnable(GL_BLEND); @@ -197,29 +234,32 @@ void OpenGLState::Apply() const { glDisable(GL_BLEND); } } + if (blend.enabled) { + if (blend_changed || blend.color.red != cur_state.blend.color.red || + blend.color.green != cur_state.blend.color.green || + blend.color.blue != cur_state.blend.color.blue || + blend.color.alpha != cur_state.blend.color.alpha) { + glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); + } - if (blend.color.red != cur_state.blend.color.red || - blend.color.green != cur_state.blend.color.green || - blend.color.blue != cur_state.blend.color.blue || - blend.color.alpha != cur_state.blend.color.alpha) { - glBlendColor(blend.color.red, blend.color.green, blend.color.blue, blend.color.alpha); - } - - if (blend.src_rgb_func != cur_state.blend.src_rgb_func || - blend.dst_rgb_func != cur_state.blend.dst_rgb_func || - blend.src_a_func != cur_state.blend.src_a_func || - blend.dst_a_func != cur_state.blend.dst_a_func) { - glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, - blend.dst_a_func); - } + if (blend_changed || blend.src_rgb_func != cur_state.blend.src_rgb_func || + blend.dst_rgb_func != cur_state.blend.dst_rgb_func || + blend.src_a_func != cur_state.blend.src_a_func || + blend.dst_a_func != cur_state.blend.dst_a_func) { + glBlendFuncSeparate(blend.src_rgb_func, blend.dst_rgb_func, blend.src_a_func, + blend.dst_a_func); + } - if (blend.rgb_equation != cur_state.blend.rgb_equation || - blend.a_equation != cur_state.blend.a_equation) { - glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); + if (blend_changed || blend.rgb_equation != cur_state.blend.rgb_equation || + blend.a_equation != cur_state.blend.a_equation) { + glBlendEquationSeparate(blend.rgb_equation, blend.a_equation); + } } +} - // Logic Operation - if (logic_op.enabled != cur_state.logic_op.enabled) { +void OpenGLState::ApplyLogicOp() const { + const bool logic_op_changed = logic_op.enabled != cur_state.logic_op.enabled; + if (logic_op_changed) { if (logic_op.enabled) { ASSERT(!blend.enabled); glEnable(GL_COLOR_LOGIC_OP); @@ -228,11 +268,13 @@ void OpenGLState::Apply() const { } } - if (logic_op.operation != cur_state.logic_op.operation) { + if (logic_op.enabled && + (logic_op_changed || logic_op.operation != cur_state.logic_op.operation)) { glLogicOp(logic_op.operation); } +} - // Textures +void OpenGLState::ApplyTextures() const { for (std::size_t i = 0; i < std::size(texture_units); ++i) { const auto& texture_unit = texture_units[i]; const auto& cur_state_texture_unit = cur_state.texture_units[i]; @@ -251,28 +293,29 @@ void OpenGLState::Apply() const { glTexParameteriv(texture_unit.target, GL_TEXTURE_SWIZZLE_RGBA, mask.data()); } } +} - // Samplers - { - bool has_delta{}; - std::size_t first{}, last{}; - std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; - for (std::size_t i = 0; i < std::size(samplers); ++i) { - samplers[i] = texture_units[i].sampler; - if (samplers[i] != cur_state.texture_units[i].sampler) { - if (!has_delta) { - first = i; - has_delta = true; - } - last = i; +void OpenGLState::ApplySamplers() const { + bool has_delta{}; + std::size_t first{}, last{}; + std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; + for (std::size_t i = 0; i < std::size(samplers); ++i) { + samplers[i] = texture_units[i].sampler; + if (samplers[i] != cur_state.texture_units[i].sampler) { + if (!has_delta) { + first = i; + has_delta = true; } - } - if (has_delta) { - glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), - samplers.data()); + last = i; } } + if (has_delta) { + glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), + samplers.data()); + } +} +void OpenGLState::Apply() const { // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); @@ -305,27 +348,12 @@ void OpenGLState::Apply() const { if (draw.program_pipeline != cur_state.draw.program_pipeline) { glBindProgramPipeline(draw.program_pipeline); } - - // Scissor test - if (scissor.enabled != cur_state.scissor.enabled) { - if (scissor.enabled) { - glEnable(GL_SCISSOR_TEST); - } else { - glDisable(GL_SCISSOR_TEST); - } - } - - if (scissor.x != cur_state.scissor.x || scissor.y != cur_state.scissor.y || - scissor.width != cur_state.scissor.width || scissor.height != cur_state.scissor.height) { - glScissor(scissor.x, scissor.y, scissor.width, scissor.height); - } - + // Viewport if (viewport.x != cur_state.viewport.x || viewport.y != cur_state.viewport.y || viewport.width != cur_state.viewport.width || viewport.height != cur_state.viewport.height) { glViewport(viewport.x, viewport.y, viewport.width, viewport.height); } - // Clip distance for (std::size_t i = 0; i < clip_distance.size(); ++i) { if (clip_distance[i] != cur_state.clip_distance[i]) { @@ -336,12 +364,28 @@ void OpenGLState::Apply() const { } } } - + // Color mask + if (color_mask.red_enabled != cur_state.color_mask.red_enabled || + color_mask.green_enabled != cur_state.color_mask.green_enabled || + color_mask.blue_enabled != cur_state.color_mask.blue_enabled || + color_mask.alpha_enabled != cur_state.color_mask.alpha_enabled) { + glColorMask(color_mask.red_enabled, color_mask.green_enabled, color_mask.blue_enabled, + color_mask.alpha_enabled); + } // Point if (point.size != cur_state.point.size) { glPointSize(point.size); } - + ApplyScissorTest(); + ApplyStencilTest(); + ApplySRgb(); + ApplyCulling(); + ApplyDepth(); + ApplyPrimitiveRestart(); + ApplyBlending(); + ApplyLogicOp(); + ApplyTextures(); + ApplySamplers(); cur_state = *this; } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 9e2c573b5..fe648aff6 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -173,7 +173,8 @@ public: } /// Apply this state as the current OpenGL state void Apply() const; - + /// Set the initial OpenGL state + static void ApplyDefaultState(); /// Resets any references to the given resource OpenGLState& UnbindTexture(GLuint handle); OpenGLState& ResetSampler(GLuint handle); @@ -188,6 +189,16 @@ private: // Workaround for sRGB problems caused by // QT not supporting srgb output static bool s_rgb_used; + void ApplySRgb() const; + void ApplyCulling() const; + void ApplyDepth() const; + void ApplyPrimitiveRestart() const; + void ApplyStencilTest() const; + void ApplyScissorTest() const; + void ApplyBlending() const; + void ApplyLogicOp() const; + void ApplyTextures() const; + void ApplySamplers() const; }; } // namespace OpenGL diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt index 9379d9110..f9ca2948e 100644 --- a/src/yuzu/CMakeLists.txt +++ b/src/yuzu/CMakeLists.txt @@ -56,6 +56,8 @@ add_executable(yuzu main.h ui_settings.cpp ui_settings.h + util/limitable_input_dialog.cpp + util/limitable_input_dialog.h util/spinbox.cpp util/spinbox.h util/util.cpp diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 1b8aa7de2..b4b4a4a56 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp @@ -6,20 +6,20 @@ #include <QFileDialog> #include <QGraphicsItem> #include <QGraphicsScene> -#include <QInputDialog> +#include <QHeaderView> #include <QMessageBox> #include <QStandardItemModel> #include <QTreeView> #include <QVBoxLayout> -#include "common/common_paths.h" -#include "common/logging/backend.h" +#include "common/assert.h" +#include "common/file_util.h" #include "common/string_util.h" #include "core/core.h" #include "core/hle/service/acc/profile_manager.h" #include "core/settings.h" #include "ui_configure_system.h" #include "yuzu/configuration/configure_system.h" -#include "yuzu/main.h" +#include "yuzu/util/limitable_input_dialog.h" namespace { constexpr std::array<int, 12> days_in_month = {{ @@ -83,6 +83,12 @@ QPixmap GetIcon(Service::Account::UUID uuid) { return icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation); } + +QString GetProfileUsernameFromUser(QWidget* parent, const QString& description_text) { + return LimitableInputDialog::GetText(parent, ConfigureSystem::tr("Enter Username"), + description_text, 1, + static_cast<int>(Service::Account::profile_username_size)); +} } // Anonymous namespace ConfigureSystem::ConfigureSystem(QWidget* parent) @@ -244,15 +250,13 @@ void ConfigureSystem::SelectUser(const QModelIndex& index) { } void ConfigureSystem::AddUser() { - const auto uuid = Service::Account::UUID::Generate(); - - bool ok = false; const auto username = - QInputDialog::getText(this, tr("Enter Username"), tr("Enter a username for the new user:"), - QLineEdit::Normal, QString(), &ok); - if (!ok) + GetProfileUsernameFromUser(this, tr("Enter a username for the new user:")); + if (username.isEmpty()) { return; + } + const auto uuid = Service::Account::UUID::Generate(); profile_manager->CreateNewUser(uuid, username.toStdString()); item_model->appendRow(new QStandardItem{GetIcon(uuid), FormatUserEntryText(username, uuid)}); @@ -267,23 +271,14 @@ void ConfigureSystem::RenameUser() { if (!profile_manager->GetProfileBase(*uuid, profile)) return; - bool ok = false; - const auto old_username = GetAccountUsername(*profile_manager, *uuid); - const auto new_username = - QInputDialog::getText(this, tr("Enter Username"), tr("Enter a new username:"), - QLineEdit::Normal, old_username, &ok); - - if (!ok) + const auto new_username = GetProfileUsernameFromUser(this, tr("Enter a new username:")); + if (new_username.isEmpty()) { return; + } - std::fill(profile.username.begin(), profile.username.end(), '\0'); const auto username_std = new_username.toStdString(); - if (username_std.size() > profile.username.size()) { - std::copy_n(username_std.begin(), std::min(profile.username.size(), username_std.size()), - profile.username.begin()); - } else { - std::copy(username_std.begin(), username_std.end(), profile.username.begin()); - } + std::fill(profile.username.begin(), profile.username.end(), '\0'); + std::copy(username_std.begin(), username_std.end(), profile.username.begin()); profile_manager->SetProfileBase(*uuid, profile); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c5a56cbfd..47d52c385 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -1532,7 +1532,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { "derivation. It will be attempted but may not complete.<br><br>") + errors + tr("<br><br>You can get all of these and dump all of your games easily by " - "following <a href='https://yuzu-emu.org/help/quickstart/quickstart/'>the " + "following <a href='https://yuzu-emu.org/help/quickstart/'>the " "quickstart guide</a>. Alternatively, you can use another method of dumping " "to obtain all of your keys.")); } diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp new file mode 100644 index 000000000..edd78e579 --- /dev/null +++ b/src/yuzu/util/limitable_input_dialog.cpp @@ -0,0 +1,59 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <QDialogButtonBox> +#include <QLabel> +#include <QLineEdit> +#include <QPushButton> +#include <QVBoxLayout> +#include "yuzu/util/limitable_input_dialog.h" + +LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} { + CreateUI(); + ConnectEvents(); +} + +LimitableInputDialog::~LimitableInputDialog() = default; + +void LimitableInputDialog::CreateUI() { + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + text_label = new QLabel(this); + text_entry = new QLineEdit(this); + buttons = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + + auto* const layout = new QVBoxLayout; + layout->addWidget(text_label); + layout->addWidget(text_entry); + layout->addWidget(buttons); + + setLayout(layout); +} + +void LimitableInputDialog::ConnectEvents() { + connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject); +} + +QString LimitableInputDialog::GetText(QWidget* parent, const QString& title, const QString& text, + int min_character_limit, int max_character_limit) { + Q_ASSERT(min_character_limit <= max_character_limit); + + LimitableInputDialog dialog{parent}; + dialog.setWindowTitle(title); + dialog.text_label->setText(text); + dialog.text_entry->setMaxLength(max_character_limit); + + auto* const ok_button = dialog.buttons->button(QDialogButtonBox::Ok); + ok_button->setEnabled(false); + connect(dialog.text_entry, &QLineEdit::textEdited, [&](const QString& new_text) { + ok_button->setEnabled(new_text.length() >= min_character_limit); + }); + + if (dialog.exec() != QDialog::Accepted) { + return {}; + } + + return dialog.text_entry->text(); +} diff --git a/src/yuzu/util/limitable_input_dialog.h b/src/yuzu/util/limitable_input_dialog.h new file mode 100644 index 000000000..164ad7301 --- /dev/null +++ b/src/yuzu/util/limitable_input_dialog.h @@ -0,0 +1,31 @@ +// Copyright 2018 yuzu Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <QDialog> + +class QDialogButtonBox; +class QLabel; +class QLineEdit; + +/// A QDialog that functions similarly to QInputDialog, however, it allows +/// restricting the minimum and total number of characters that can be entered. +class LimitableInputDialog final : public QDialog { + Q_OBJECT +public: + explicit LimitableInputDialog(QWidget* parent = nullptr); + ~LimitableInputDialog() override; + + static QString GetText(QWidget* parent, const QString& title, const QString& text, + int min_character_limit, int max_character_limit); + +private: + void CreateUI(); + void ConnectEvents(); + + QLabel* text_label; + QLineEdit* text_entry; + QDialogButtonBox* buttons; +}; |