Qt: Implement per-game controller configuration
This commit is contained in:
parent
9e3507e0f4
commit
bda6869084
|
@ -10,7 +10,6 @@ class LayeredSettingsInterface final : public SettingsInterface
|
|||
public:
|
||||
enum Layer : u32
|
||||
{
|
||||
LAYER_CMDLINE,
|
||||
LAYER_GAME,
|
||||
LAYER_INPUT,
|
||||
LAYER_BASE,
|
||||
|
@ -66,7 +65,7 @@ public:
|
|||
using SettingsInterface::GetUIntValue;
|
||||
|
||||
private:
|
||||
static constexpr Layer FIRST_LAYER = LAYER_CMDLINE;
|
||||
static constexpr Layer FIRST_LAYER = LAYER_GAME;
|
||||
static constexpr Layer LAST_LAYER = LAYER_BASE;
|
||||
|
||||
std::array<SettingsInterface*, NUM_LAYERS> m_layers{};
|
||||
|
|
|
@ -10,7 +10,7 @@ class MemorySettingsInterface final : public SettingsInterface
|
|||
{
|
||||
public:
|
||||
MemorySettingsInterface();
|
||||
~MemorySettingsInterface();
|
||||
~MemorySettingsInterface() override;
|
||||
|
||||
bool Save(Error* error = nullptr) override;
|
||||
|
||||
|
|
|
@ -2798,7 +2798,7 @@ void FullscreenUI::DoCopyGameSettings()
|
|||
return;
|
||||
|
||||
Settings temp_settings;
|
||||
temp_settings.Load(*GetEditingSettingsInterface(false));
|
||||
temp_settings.Load(*GetEditingSettingsInterface(false), *GetEditingSettingsInterface(false));
|
||||
temp_settings.Save(*s_game_settings_interface, true);
|
||||
SetSettingsChanged(s_game_settings_interface.get());
|
||||
|
||||
|
@ -3652,20 +3652,20 @@ void FullscreenUI::DrawControllerSettingsPage()
|
|||
if (IsEditingGameSettings(bsi))
|
||||
{
|
||||
if (DrawToggleSetting(bsi, FSUI_ICONSTR(ICON_FA_COG, "Per-Game Configuration"),
|
||||
FSUI_CSTR("Uses game-specific settings for controllers for this game."), "Pad",
|
||||
FSUI_CSTR("Uses game-specific settings for controllers for this game."), "ControllerPorts",
|
||||
"UseGameSettingsForController", false, IsEditingGameSettings(bsi), false))
|
||||
{
|
||||
// did we just enable per-game for the first time?
|
||||
if (bsi->GetBoolValue("Pad", "UseGameSettingsForController", false) &&
|
||||
!bsi->GetBoolValue("Pad", "GameSettingsInitialized", false))
|
||||
if (bsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false) &&
|
||||
!bsi->GetBoolValue("ControllerPorts", "GameSettingsInitialized", false))
|
||||
{
|
||||
bsi->SetBoolValue("Pad", "GameSettingsInitialized", true);
|
||||
bsi->SetBoolValue("ControllerPorts", "GameSettingsInitialized", true);
|
||||
CopyGlobalControllerSettingsToGame();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (IsEditingGameSettings(bsi) && !bsi->GetBoolValue("Pad", "UseGameSettingsForController", false))
|
||||
if (IsEditingGameSettings(bsi) && !bsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
|
||||
{
|
||||
// nothing to edit..
|
||||
EndMenuButtons();
|
||||
|
|
|
@ -134,7 +134,7 @@ void Settings::UpdateOverclockActive()
|
|||
cpu_overclock_active = (cpu_overclock_enable && (cpu_overclock_numerator != 1 || cpu_overclock_denominator != 1));
|
||||
}
|
||||
|
||||
void Settings::Load(SettingsInterface& si)
|
||||
void Settings::Load(SettingsInterface& si, SettingsInterface& controller_si)
|
||||
{
|
||||
region =
|
||||
ParseConsoleRegionName(
|
||||
|
@ -364,7 +364,8 @@ void Settings::Load(SettingsInterface& si)
|
|||
|
||||
multitap_mode =
|
||||
ParseMultitapModeName(
|
||||
si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE)).c_str())
|
||||
controller_si.GetStringValue("ControllerPorts", "MultitapMode", GetMultitapModeName(DEFAULT_MULTITAP_MODE))
|
||||
.c_str())
|
||||
.value_or(DEFAULT_MULTITAP_MODE);
|
||||
|
||||
const std::array<bool, 2> mtap_enabled = {{IsPort1MultitapEnabled(), IsPort2MultitapEnabled()}};
|
||||
|
@ -379,7 +380,7 @@ void Settings::Load(SettingsInterface& si)
|
|||
}
|
||||
|
||||
const ControllerType default_type = (i == 0) ? DEFAULT_CONTROLLER_1_TYPE : DEFAULT_CONTROLLER_2_TYPE;
|
||||
const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(si.GetTinyStringValue(
|
||||
const Controller::ControllerInfo* cinfo = Controller::GetControllerInfo(controller_si.GetTinyStringValue(
|
||||
Controller::GetSettingsSection(i).c_str(), "Type", Controller::GetControllerInfo(default_type)->name));
|
||||
controller_types[i] = cinfo ? cinfo->type : default_type;
|
||||
}
|
||||
|
|
|
@ -354,7 +354,7 @@ struct Settings
|
|||
DEFAULT_VRAM_WRITE_DUMP_HEIGHT_THRESHOLD = 128,
|
||||
};
|
||||
|
||||
void Load(SettingsInterface& si);
|
||||
void Load(SettingsInterface& si, SettingsInterface& controller_si);
|
||||
void Save(SettingsInterface& si, bool ignore_base) const;
|
||||
static void Clear(SettingsInterface& si);
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
#include "common/dynamic_library.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/layered_settings_interface.h"
|
||||
#include "common/log.h"
|
||||
#include "common/path.h"
|
||||
#include "common/string_util.h"
|
||||
|
@ -136,7 +137,8 @@ struct MemorySaveState
|
|||
static void CheckCacheLineSize();
|
||||
static void LogStartupInformation();
|
||||
|
||||
static void LoadInputBindings(SettingsInterface& si, std::unique_lock<std::mutex>& lock);
|
||||
static LayeredSettingsInterface GetControllerSettingsLayers(std::unique_lock<std::mutex>& lock);
|
||||
static LayeredSettingsInterface GetHotkeySettingsLayer(std::unique_lock<std::mutex>& lock);
|
||||
|
||||
static std::string GetExecutableNameForImage(IsoReader& iso, bool strip_subdirectories);
|
||||
static bool ReadExecutableFromImage(IsoReader& iso, std::string* out_executable_name,
|
||||
|
@ -1202,12 +1204,14 @@ void System::LoadSettings(bool display_osd_messages)
|
|||
{
|
||||
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
||||
SettingsInterface& si = *Host::GetSettingsInterface();
|
||||
g_settings.Load(si);
|
||||
LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock);
|
||||
LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock);
|
||||
g_settings.Load(si, controller_si);
|
||||
g_settings.UpdateLogSettings();
|
||||
|
||||
Host::LoadSettings(si, lock);
|
||||
InputManager::ReloadSources(si, lock);
|
||||
LoadInputBindings(si, lock);
|
||||
InputManager::ReloadSources(controller_si, lock);
|
||||
InputManager::ReloadBindings(controller_si, hotkey_si);
|
||||
WarnAboutUnsafeSettings();
|
||||
|
||||
// apply compatibility settings
|
||||
|
@ -1224,12 +1228,15 @@ void System::LoadSettings(bool display_osd_messages)
|
|||
void System::ReloadInputSources()
|
||||
{
|
||||
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
||||
SettingsInterface* si = Host::GetSettingsInterface();
|
||||
InputManager::ReloadSources(*si, lock);
|
||||
LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock);
|
||||
InputManager::ReloadSources(controller_si, lock);
|
||||
|
||||
// skip loading bindings if we're not running, since it'll get done on startup anyway
|
||||
if (IsValid())
|
||||
LoadInputBindings(*si, lock);
|
||||
{
|
||||
LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock);
|
||||
InputManager::ReloadBindings(controller_si, hotkey_si);
|
||||
}
|
||||
}
|
||||
|
||||
void System::ReloadInputBindings()
|
||||
|
@ -1239,37 +1246,43 @@ void System::ReloadInputBindings()
|
|||
return;
|
||||
|
||||
std::unique_lock<std::mutex> lock = Host::GetSettingsLock();
|
||||
SettingsInterface* si = Host::GetSettingsInterface();
|
||||
LoadInputBindings(*si, lock);
|
||||
LayeredSettingsInterface controller_si = GetControllerSettingsLayers(lock);
|
||||
LayeredSettingsInterface hotkey_si = GetHotkeySettingsLayer(lock);
|
||||
InputManager::ReloadBindings(controller_si, hotkey_si);
|
||||
}
|
||||
|
||||
void System::LoadInputBindings(SettingsInterface& si, std::unique_lock<std::mutex>& lock)
|
||||
LayeredSettingsInterface System::GetControllerSettingsLayers(std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
// Hotkeys use the base configuration, except if the custom hotkeys option is enabled.
|
||||
LayeredSettingsInterface ret;
|
||||
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_BASE, Host::Internal::GetBaseSettingsLayer());
|
||||
|
||||
// Select input profile _or_ game settings, not both.
|
||||
if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer())
|
||||
{
|
||||
const bool use_profile_hotkeys = isi->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false);
|
||||
if (use_profile_hotkeys)
|
||||
{
|
||||
InputManager::ReloadBindings(si, *isi, *isi);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Temporarily disable the input profile layer, so it doesn't take precedence.
|
||||
Host::Internal::SetInputSettingsLayer(nullptr, lock);
|
||||
InputManager::ReloadBindings(si, *isi, si);
|
||||
Host::Internal::SetInputSettingsLayer(s_input_settings_interface.get(), lock);
|
||||
}
|
||||
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer());
|
||||
}
|
||||
else if (SettingsInterface* gsi = Host::Internal::GetGameSettingsLayer();
|
||||
gsi && gsi->GetBoolValue("ControllerPorts", "UseGameSettingsForController", false))
|
||||
{
|
||||
InputManager::ReloadBindings(si, *gsi, si);
|
||||
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_GAME, gsi);
|
||||
}
|
||||
else
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
LayeredSettingsInterface System::GetHotkeySettingsLayer(std::unique_lock<std::mutex>& lock)
|
||||
{
|
||||
LayeredSettingsInterface ret;
|
||||
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_BASE, Host::Internal::GetBaseSettingsLayer());
|
||||
|
||||
// Only add input profile layer if the option is enabled.
|
||||
if (SettingsInterface* isi = Host::Internal::GetInputSettingsLayer();
|
||||
isi && isi->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false))
|
||||
{
|
||||
InputManager::ReloadBindings(si, si, si);
|
||||
ret.SetLayer(LayeredSettingsInterface::Layer::LAYER_INPUT, Host::Internal::GetInputSettingsLayer());
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void System::SetDefaultSettings(SettingsInterface& si)
|
||||
|
|
|
@ -247,7 +247,7 @@ void ControllerBindingWidget::onTypeChanged()
|
|||
m_controller_info = Controller::GetControllerInfo(static_cast<ControllerType>(index));
|
||||
DebugAssert(m_controller_info);
|
||||
|
||||
SettingsInterface* sif = m_dialog->getProfileSettingsInterface();
|
||||
SettingsInterface* sif = m_dialog->getEditingSettingsInterface();
|
||||
if (sif)
|
||||
{
|
||||
sif->SetStringValue(m_config_section.c_str(), "Type", m_controller_info->name);
|
||||
|
@ -307,7 +307,7 @@ void ControllerBindingWidget::onClearBindingsClicked()
|
|||
}
|
||||
else
|
||||
{
|
||||
InputManager::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number);
|
||||
InputManager::ClearPortBindings(*m_dialog->getEditingSettingsInterface(), m_port_number);
|
||||
}
|
||||
|
||||
saveAndRefresh();
|
||||
|
@ -358,8 +358,8 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device)
|
|||
}
|
||||
else
|
||||
{
|
||||
result = InputManager::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
|
||||
QtHost::SaveGameSettings(m_dialog->getProfileSettingsInterface(), false);
|
||||
result = InputManager::MapController(*m_dialog->getEditingSettingsInterface(), m_port_number, mapping);
|
||||
QtHost::SaveGameSettings(m_dialog->getEditingSettingsInterface(), false);
|
||||
g_emu_thread->reloadInputBindings();
|
||||
}
|
||||
|
||||
|
@ -377,7 +377,7 @@ void ControllerBindingWidget::saveAndRefresh()
|
|||
|
||||
void ControllerBindingWidget::createBindingWidgets(QWidget* parent)
|
||||
{
|
||||
SettingsInterface* sif = getDialog()->getProfileSettingsInterface();
|
||||
SettingsInterface* sif = getDialog()->getEditingSettingsInterface();
|
||||
DebugAssert(m_controller_info);
|
||||
|
||||
QGroupBox* axis_gbox = nullptr;
|
||||
|
@ -471,7 +471,7 @@ void ControllerBindingWidget::createBindingWidgets(QWidget* parent)
|
|||
|
||||
void ControllerBindingWidget::bindBindingWidgets(QWidget* parent)
|
||||
{
|
||||
SettingsInterface* sif = getDialog()->getProfileSettingsInterface();
|
||||
SettingsInterface* sif = getDialog()->getEditingSettingsInterface();
|
||||
DebugAssert(m_controller_info);
|
||||
|
||||
const std::string& config_section = getConfigSection();
|
||||
|
@ -601,12 +601,12 @@ ControllerMacroEditWidget::ControllerMacroEditWidget(ControllerMacroWidget* pare
|
|||
}
|
||||
|
||||
m_frequency = dialog->getIntValue(section.c_str(), TinyString::from_format("Macro{}Frequency", index + 1u), 0);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(dialog->getProfileSettingsInterface(), m_ui.triggerToggle,
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(dialog->getEditingSettingsInterface(), m_ui.triggerToggle,
|
||||
section.c_str(), fmt::format("Macro{}Toggle", index + 1u),
|
||||
false);
|
||||
updateFrequencyText();
|
||||
|
||||
m_ui.trigger->initialize(dialog->getProfileSettingsInterface(), InputBindingInfo::Type::Macro, section,
|
||||
m_ui.trigger->initialize(dialog->getEditingSettingsInterface(), InputBindingInfo::Type::Macro, section,
|
||||
fmt::format("Macro{}", index + 1u));
|
||||
|
||||
connect(m_ui.increaseFrequency, &QAbstractButton::clicked, this, [this]() { modFrequency(1); });
|
||||
|
@ -747,7 +747,7 @@ void ControllerCustomSettingsWidget::createSettingWidgets(ControllerBindingWidge
|
|||
QGridLayout* layout, const Controller::ControllerInfo* cinfo)
|
||||
{
|
||||
const std::string& section = parent->getConfigSection();
|
||||
SettingsInterface* sif = parent->getDialog()->getProfileSettingsInterface();
|
||||
SettingsInterface* sif = parent->getDialog()->getEditingSettingsInterface();
|
||||
int current_row = 0;
|
||||
|
||||
for (const SettingInfo& si : cinfo->settings)
|
||||
|
|
|
@ -14,7 +14,7 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
|||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
SettingsInterface* sif = dialog->getProfileSettingsInterface();
|
||||
SettingsInterface* sif = dialog->getEditingSettingsInterface();
|
||||
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true);
|
||||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources",
|
||||
|
@ -28,10 +28,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLMFIDriver, "InputSources", "SDLMFIDriver", true);
|
||||
#else
|
||||
m_ui.sdlGridLayout->removeWidget(m_ui.enableSDLIOKitDriver);
|
||||
m_ui.enableSDLIOKitDriver->deleteLater();
|
||||
delete m_ui.enableSDLIOKitDriver;
|
||||
m_ui.enableSDLIOKitDriver = nullptr;
|
||||
m_ui.sdlGridLayout->removeWidget(m_ui.enableSDLMFIDriver);
|
||||
m_ui.enableSDLMFIDriver->deleteLater();
|
||||
delete m_ui.enableSDLMFIDriver;
|
||||
m_ui.enableSDLMFIDriver = nullptr;
|
||||
#endif
|
||||
|
||||
|
@ -41,10 +41,10 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
|||
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableRawInput, "InputSources", "RawInput", false);
|
||||
#else
|
||||
m_ui.mainLayout->removeWidget(m_ui.xinputGroup);
|
||||
m_ui.xinputGroup->deleteLater();
|
||||
delete m_ui.xinputGroup;
|
||||
m_ui.xinputGroup = nullptr;
|
||||
m_ui.mainLayout->removeWidget(m_ui.dinputGroup);
|
||||
m_ui.dinputGroup->deleteLater();
|
||||
delete m_ui.dinputGroup;
|
||||
m_ui.dinputGroup = nullptr;
|
||||
#endif
|
||||
|
||||
|
@ -71,10 +71,13 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
|
|||
{
|
||||
// remove profile options from the UI.
|
||||
m_ui.mainLayout->removeWidget(m_ui.profileSettings);
|
||||
m_ui.profileSettings->deleteLater();
|
||||
delete m_ui.profileSettings;
|
||||
m_ui.profileSettings = nullptr;
|
||||
}
|
||||
|
||||
if (dialog->isEditingGameSettings())
|
||||
m_ui.deviceListGroup->setEnabled(false);
|
||||
|
||||
connect(m_ui.multitapMode, &QComboBox::currentIndexChanged, this, [this]() { emit bindingSetupChanged(); });
|
||||
|
||||
connect(m_ui.pointerXScale, &QSlider::valueChanged, this,
|
||||
|
@ -134,9 +137,10 @@ ControllerLEDSettingsDialog::ControllerLEDSettingsDialog(QWidget* parent, Contro
|
|||
linkButton(m_ui.SDL2LED, 2);
|
||||
linkButton(m_ui.SDL3LED, 3);
|
||||
|
||||
SettingsInterface* sif = dialog->getProfileSettingsInterface();
|
||||
SettingsInterface* sif = dialog->getEditingSettingsInterface();
|
||||
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources", "SDLPS5PlayerLED", false);
|
||||
ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLPS5PlayerLED, "InputSources",
|
||||
"SDLPS5PlayerLED", false);
|
||||
connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@
|
|||
</widget>
|
||||
</item>
|
||||
<item row="0" column="1" rowspan="7">
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<widget class="QGroupBox" name="deviceListGroup">
|
||||
<property name="title">
|
||||
<string>Detected Devices</string>
|
||||
</property>
|
||||
|
@ -162,8 +162,7 @@
|
|||
<string>Controller LED Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="lightbulb-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="lightbulb-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -284,7 +283,7 @@
|
|||
<number>30</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -339,7 +338,7 @@
|
|||
<number>30</number>
|
||||
</property>
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
@ -388,7 +387,7 @@
|
|||
<item row="6" column="0">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
<enum>Qt::Orientation::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
|
|
@ -23,41 +23,87 @@
|
|||
|
||||
static constexpr const std::array<char, 4> s_mtap_slot_names = {{'A', 'B', 'C', 'D'}};
|
||||
|
||||
ControllerSettingsWindow::ControllerSettingsWindow() : QWidget()
|
||||
ControllerSettingsWindow::ControllerSettingsWindow(SettingsInterface* game_sif /* = nullptr */,
|
||||
QWidget* parent /* = nullptr */)
|
||||
: QWidget(parent), m_editing_settings_interface(game_sif)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
refreshProfileList();
|
||||
createWidgets();
|
||||
|
||||
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
|
||||
|
||||
connect(m_ui.settingsCategory, &QListWidget::currentRowChanged, this,
|
||||
&ControllerSettingsWindow::onCategoryCurrentRowChanged);
|
||||
connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this,
|
||||
&ControllerSettingsWindow::onCurrentProfileChanged);
|
||||
connect(m_ui.buttonBox, &QDialogButtonBox::rejected, this, &ControllerSettingsWindow::close);
|
||||
connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked);
|
||||
connect(m_ui.applyProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onApplyProfileClicked);
|
||||
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked);
|
||||
connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked);
|
||||
|
||||
connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this,
|
||||
&ControllerSettingsWindow::onInputDevicesEnumerated);
|
||||
connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsWindow::onInputDeviceConnected);
|
||||
connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this,
|
||||
&ControllerSettingsWindow::onInputDeviceDisconnected);
|
||||
connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this,
|
||||
&ControllerSettingsWindow::onVibrationMotorsEnumerated);
|
||||
if (!game_sif)
|
||||
{
|
||||
refreshProfileList();
|
||||
|
||||
// trigger a device enumeration to populate the device list
|
||||
g_emu_thread->enumerateInputDevices();
|
||||
g_emu_thread->enumerateVibrationMotors();
|
||||
m_ui.editProfileLayout->removeWidget(m_ui.copyGlobalSettings);
|
||||
delete m_ui.copyGlobalSettings;
|
||||
m_ui.copyGlobalSettings = nullptr;
|
||||
|
||||
connect(m_ui.currentProfile, &QComboBox::currentIndexChanged, this,
|
||||
&ControllerSettingsWindow::onCurrentProfileChanged);
|
||||
connect(m_ui.newProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onNewProfileClicked);
|
||||
connect(m_ui.applyProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onApplyProfileClicked);
|
||||
connect(m_ui.deleteProfile, &QPushButton::clicked, this, &ControllerSettingsWindow::onDeleteProfileClicked);
|
||||
connect(m_ui.restoreDefaults, &QPushButton::clicked, this, &ControllerSettingsWindow::onRestoreDefaultsClicked);
|
||||
|
||||
connect(g_emu_thread, &EmuThread::onInputDevicesEnumerated, this,
|
||||
&ControllerSettingsWindow::onInputDevicesEnumerated);
|
||||
connect(g_emu_thread, &EmuThread::onInputDeviceConnected, this, &ControllerSettingsWindow::onInputDeviceConnected);
|
||||
connect(g_emu_thread, &EmuThread::onInputDeviceDisconnected, this,
|
||||
&ControllerSettingsWindow::onInputDeviceDisconnected);
|
||||
connect(g_emu_thread, &EmuThread::onVibrationMotorsEnumerated, this,
|
||||
&ControllerSettingsWindow::onVibrationMotorsEnumerated);
|
||||
|
||||
// trigger a device enumeration to populate the device list
|
||||
g_emu_thread->enumerateInputDevices();
|
||||
g_emu_thread->enumerateVibrationMotors();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.editProfileLayout->removeWidget(m_ui.editProfileLabel);
|
||||
delete m_ui.editProfileLabel;
|
||||
m_ui.editProfileLabel = nullptr;
|
||||
m_ui.editProfileLayout->removeWidget(m_ui.currentProfile);
|
||||
delete m_ui.currentProfile;
|
||||
m_ui.currentProfile = nullptr;
|
||||
m_ui.editProfileLayout->removeWidget(m_ui.newProfile);
|
||||
delete m_ui.newProfile;
|
||||
m_ui.newProfile = nullptr;
|
||||
m_ui.editProfileLayout->removeWidget(m_ui.applyProfile);
|
||||
delete m_ui.applyProfile;
|
||||
m_ui.applyProfile = nullptr;
|
||||
m_ui.editProfileLayout->removeWidget(m_ui.deleteProfile);
|
||||
delete m_ui.deleteProfile;
|
||||
m_ui.deleteProfile = nullptr;
|
||||
|
||||
connect(m_ui.copyGlobalSettings, &QPushButton::clicked, this,
|
||||
&ControllerSettingsWindow::onCopyGlobalSettingsClicked);
|
||||
connect(m_ui.restoreDefaults, &QPushButton::clicked, this,
|
||||
&ControllerSettingsWindow::onRestoreDefaultsForGameClicked);
|
||||
}
|
||||
|
||||
createWidgets();
|
||||
}
|
||||
|
||||
ControllerSettingsWindow::~ControllerSettingsWindow() = default;
|
||||
|
||||
void ControllerSettingsWindow::editControllerSettingsForGame(QWidget* parent, SettingsInterface* sif)
|
||||
{
|
||||
ControllerSettingsWindow* dlg = new ControllerSettingsWindow(sif, parent);
|
||||
dlg->setWindowFlag(Qt::Window);
|
||||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||||
dlg->setWindowModality(Qt::WindowModality::WindowModal);
|
||||
dlg->setWindowTitle(parent->windowTitle());
|
||||
dlg->setWindowIcon(parent->windowIcon());
|
||||
dlg->show();
|
||||
}
|
||||
|
||||
int ControllerSettingsWindow::getHotkeyCategoryIndex() const
|
||||
{
|
||||
const std::array<bool, 2> mtap_enabled = getEnabledMultitaps();
|
||||
|
@ -103,20 +149,26 @@ void ControllerSettingsWindow::onCategoryCurrentRowChanged(int row)
|
|||
|
||||
void ControllerSettingsWindow::onCurrentProfileChanged(int index)
|
||||
{
|
||||
switchProfile((index == 0) ? 0 : m_ui.currentProfile->itemText(index));
|
||||
std::string profile_name;
|
||||
if (index > 0)
|
||||
profile_name = m_ui.currentProfile->itemText(index).toStdString();
|
||||
|
||||
switchProfile(profile_name);
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::onNewProfileClicked()
|
||||
{
|
||||
const QString profile_name(
|
||||
QInputDialog::getText(this, tr("Create Input Profile"), tr("Enter the name for the new input profile:")));
|
||||
if (profile_name.isEmpty())
|
||||
const std::string profile_name =
|
||||
QInputDialog::getText(this, tr("Create Input Profile"), tr("Enter the name for the new input profile:"))
|
||||
.toStdString();
|
||||
if (profile_name.empty())
|
||||
return;
|
||||
|
||||
std::string profile_path(System::GetInputProfilePath(profile_name.toStdString()));
|
||||
std::string profile_path = System::GetInputProfilePath(profile_name);
|
||||
if (FileSystem::FileExists(profile_path.c_str()))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("A profile with the name '%1' already exists.").arg(profile_name));
|
||||
QMessageBox::critical(this, tr("Error"),
|
||||
tr("A profile with the name '%1' already exists.").arg(QString::fromStdString(profile_name)));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -131,7 +183,7 @@ void ControllerSettingsWindow::onNewProfileClicked()
|
|||
if (res == QMessageBox::Yes)
|
||||
{
|
||||
// copy from global or the current profile
|
||||
if (!m_profile_interface)
|
||||
if (!m_editing_settings_interface)
|
||||
{
|
||||
const int hkres = QMessageBox::question(
|
||||
this, tr("Create Input Profile"),
|
||||
|
@ -153,9 +205,9 @@ void ControllerSettingsWindow::onNewProfileClicked()
|
|||
{
|
||||
// from profile
|
||||
const bool copy_hotkey_bindings =
|
||||
m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false);
|
||||
m_editing_settings_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false);
|
||||
temp_si.SetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", copy_hotkey_bindings);
|
||||
InputManager::CopyConfiguration(&temp_si, *m_profile_interface, true, true, copy_hotkey_bindings);
|
||||
InputManager::CopyConfiguration(&temp_si, *m_editing_settings_interface, true, true, copy_hotkey_bindings);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,9 +236,9 @@ void ControllerSettingsWindow::onApplyProfileClicked()
|
|||
|
||||
{
|
||||
const bool copy_hotkey_bindings =
|
||||
m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false);
|
||||
m_editing_settings_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false);
|
||||
auto lock = Host::GetSettingsLock();
|
||||
InputManager::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_profile_interface, true, true,
|
||||
InputManager::CopyConfiguration(Host::Internal::GetBaseSettingsLayer(), *m_editing_settings_interface, true, true,
|
||||
copy_hotkey_bindings);
|
||||
QtHost::QueueSettingsSave();
|
||||
}
|
||||
|
@ -236,6 +288,36 @@ void ControllerSettingsWindow::onRestoreDefaultsClicked()
|
|||
switchProfile({});
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::onCopyGlobalSettingsClicked()
|
||||
{
|
||||
DebugAssert(isEditingGameSettings());
|
||||
|
||||
{
|
||||
const auto lock = Host::GetSettingsLock();
|
||||
InputManager::CopyConfiguration(m_editing_settings_interface, *Host::Internal::GetBaseSettingsLayer(), true, true,
|
||||
false);
|
||||
}
|
||||
|
||||
m_editing_settings_interface->Save();
|
||||
g_emu_thread->reloadGameSettings();
|
||||
createWidgets();
|
||||
|
||||
QMessageBox::information(QtUtils::GetRootWidget(this), tr("DuckStation Controller Settings"),
|
||||
tr("Per-game controller configuration reset to global settings."));
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::onRestoreDefaultsForGameClicked()
|
||||
{
|
||||
DebugAssert(isEditingGameSettings());
|
||||
Settings::SetDefaultControllerConfig(*m_editing_settings_interface);
|
||||
m_editing_settings_interface->Save();
|
||||
g_emu_thread->reloadGameSettings();
|
||||
createWidgets();
|
||||
|
||||
QMessageBox::information(QtUtils::GetRootWidget(this), tr("DuckStation Controller Settings"),
|
||||
tr("Per-game controller configuration reset to default settings."));
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::onInputDevicesEnumerated(const std::vector<std::pair<std::string, std::string>>& devices)
|
||||
{
|
||||
m_device_list = devices;
|
||||
|
@ -280,16 +362,16 @@ void ControllerSettingsWindow::onVibrationMotorsEnumerated(const QList<InputBind
|
|||
|
||||
bool ControllerSettingsWindow::getBoolValue(const char* section, const char* key, bool default_value) const
|
||||
{
|
||||
if (m_profile_interface)
|
||||
return m_profile_interface->GetBoolValue(section, key, default_value);
|
||||
if (m_editing_settings_interface)
|
||||
return m_editing_settings_interface->GetBoolValue(section, key, default_value);
|
||||
else
|
||||
return Host::GetBaseBoolSettingValue(section, key, default_value);
|
||||
}
|
||||
|
||||
s32 ControllerSettingsWindow::getIntValue(const char* section, const char* key, s32 default_value) const
|
||||
{
|
||||
if (m_profile_interface)
|
||||
return m_profile_interface->GetIntValue(section, key, default_value);
|
||||
if (m_editing_settings_interface)
|
||||
return m_editing_settings_interface->GetIntValue(section, key, default_value);
|
||||
else
|
||||
return Host::GetBaseIntSettingValue(section, key, default_value);
|
||||
}
|
||||
|
@ -298,8 +380,8 @@ std::string ControllerSettingsWindow::getStringValue(const char* section, const
|
|||
const char* default_value) const
|
||||
{
|
||||
std::string value;
|
||||
if (m_profile_interface)
|
||||
value = m_profile_interface->GetStringValue(section, key, default_value);
|
||||
if (m_editing_settings_interface)
|
||||
value = m_editing_settings_interface->GetStringValue(section, key, default_value);
|
||||
else
|
||||
value = Host::GetBaseStringSettingValue(section, key, default_value);
|
||||
return value;
|
||||
|
@ -307,9 +389,9 @@ std::string ControllerSettingsWindow::getStringValue(const char* section, const
|
|||
|
||||
void ControllerSettingsWindow::setBoolValue(const char* section, const char* key, bool value)
|
||||
{
|
||||
if (m_profile_interface)
|
||||
if (m_editing_settings_interface)
|
||||
{
|
||||
m_profile_interface->SetBoolValue(section, key, value);
|
||||
m_editing_settings_interface->SetBoolValue(section, key, value);
|
||||
saveAndReloadGameSettings();
|
||||
}
|
||||
else
|
||||
|
@ -322,9 +404,9 @@ void ControllerSettingsWindow::setBoolValue(const char* section, const char* key
|
|||
|
||||
void ControllerSettingsWindow::setIntValue(const char* section, const char* key, s32 value)
|
||||
{
|
||||
if (m_profile_interface)
|
||||
if (m_editing_settings_interface)
|
||||
{
|
||||
m_profile_interface->SetIntValue(section, key, value);
|
||||
m_editing_settings_interface->SetIntValue(section, key, value);
|
||||
saveAndReloadGameSettings();
|
||||
}
|
||||
else
|
||||
|
@ -337,9 +419,9 @@ void ControllerSettingsWindow::setIntValue(const char* section, const char* key,
|
|||
|
||||
void ControllerSettingsWindow::setStringValue(const char* section, const char* key, const char* value)
|
||||
{
|
||||
if (m_profile_interface)
|
||||
if (m_editing_settings_interface)
|
||||
{
|
||||
m_profile_interface->SetStringValue(section, key, value);
|
||||
m_editing_settings_interface->SetStringValue(section, key, value);
|
||||
saveAndReloadGameSettings();
|
||||
}
|
||||
else
|
||||
|
@ -352,17 +434,17 @@ void ControllerSettingsWindow::setStringValue(const char* section, const char* k
|
|||
|
||||
void ControllerSettingsWindow::saveAndReloadGameSettings()
|
||||
{
|
||||
DebugAssert(m_profile_interface);
|
||||
QtHost::SaveGameSettings(m_profile_interface.get(), false);
|
||||
DebugAssert(m_editing_settings_interface);
|
||||
QtHost::SaveGameSettings(m_editing_settings_interface, false);
|
||||
g_emu_thread->reloadGameSettings(false);
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::clearSettingValue(const char* section, const char* key)
|
||||
{
|
||||
if (m_profile_interface)
|
||||
if (m_editing_settings_interface)
|
||||
{
|
||||
m_profile_interface->DeleteValue(section, key);
|
||||
m_profile_interface->Save();
|
||||
m_editing_settings_interface->DeleteValue(section, key);
|
||||
m_editing_settings_interface->Save();
|
||||
g_emu_thread->reloadGameSettings();
|
||||
}
|
||||
else
|
||||
|
@ -434,7 +516,8 @@ void ControllerSettingsWindow::createWidgets()
|
|||
}
|
||||
|
||||
// only add hotkeys if we're editing global settings
|
||||
if (!m_profile_interface || m_profile_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false))
|
||||
if (!m_editing_settings_interface ||
|
||||
m_editing_settings_interface->GetBoolValue("ControllerPorts", "UseProfileHotkeyBindings", false))
|
||||
{
|
||||
QListWidgetItem* item = new QListWidgetItem();
|
||||
item->setText(tr("Hotkeys"));
|
||||
|
@ -444,9 +527,18 @@ void ControllerSettingsWindow::createWidgets()
|
|||
m_ui.settingsContainer->addWidget(m_hotkey_settings);
|
||||
}
|
||||
|
||||
m_ui.applyProfile->setEnabled(isEditingProfile());
|
||||
m_ui.deleteProfile->setEnabled(isEditingProfile());
|
||||
m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings());
|
||||
if (!isEditingGameSettings())
|
||||
{
|
||||
m_ui.applyProfile->setEnabled(isEditingProfile());
|
||||
m_ui.deleteProfile->setEnabled(isEditingProfile());
|
||||
m_ui.restoreDefaults->setEnabled(isEditingGlobalSettings());
|
||||
}
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::closeEvent(QCloseEvent* event)
|
||||
{
|
||||
QWidget::closeEvent(event);
|
||||
emit windowClosed();
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::updateListDescription(u32 global_slot, ControllerBindingWidget* widget)
|
||||
|
@ -501,30 +593,36 @@ void ControllerSettingsWindow::refreshProfileList()
|
|||
}
|
||||
}
|
||||
|
||||
void ControllerSettingsWindow::switchProfile(const QString& name)
|
||||
void ControllerSettingsWindow::switchProfile(const std::string_view name)
|
||||
{
|
||||
QSignalBlocker sb(m_ui.currentProfile);
|
||||
|
||||
if (!name.isEmpty())
|
||||
if (!name.empty())
|
||||
{
|
||||
std::string path(System::GetInputProfilePath(name.toStdString()));
|
||||
const QString name_qstr = QtUtils::StringViewToQString(name);
|
||||
|
||||
std::string path = System::GetInputProfilePath(name);
|
||||
if (!FileSystem::FileExists(path.c_str()))
|
||||
{
|
||||
QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name));
|
||||
QMessageBox::critical(this, tr("Error"), tr("The input profile named '%1' cannot be found.").arg(name_qstr));
|
||||
return;
|
||||
}
|
||||
|
||||
std::unique_ptr<INISettingsInterface> sif(std::make_unique<INISettingsInterface>(std::move(path)));
|
||||
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(path));
|
||||
sif->Load();
|
||||
m_profile_interface = std::move(sif);
|
||||
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name));
|
||||
|
||||
m_profile_settings_interface = std::move(sif);
|
||||
m_editing_settings_interface = m_profile_settings_interface.get();
|
||||
m_ui.currentProfile->setCurrentIndex(m_ui.currentProfile->findText(name_qstr));
|
||||
m_profile_name = name_qstr;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_profile_interface.reset();
|
||||
m_profile_settings_interface.reset();
|
||||
m_editing_settings_interface = nullptr;
|
||||
m_ui.currentProfile->setCurrentIndex(0);
|
||||
m_profile_name = QString();
|
||||
}
|
||||
|
||||
m_profile_name = name;
|
||||
createWidgets();
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class Error;
|
||||
|
||||
class ControllerGlobalSettingsWidget;
|
||||
class ControllerBindingWidget;
|
||||
class HotkeySettingsWidget;
|
||||
|
@ -44,22 +46,33 @@ public:
|
|||
MAX_PORTS = 8
|
||||
};
|
||||
|
||||
ControllerSettingsWindow();
|
||||
ControllerSettingsWindow(SettingsInterface* game_sif = nullptr, QWidget* parent = nullptr);
|
||||
~ControllerSettingsWindow();
|
||||
|
||||
static void editControllerSettingsForGame(QWidget* parent, SettingsInterface* sif);
|
||||
|
||||
ALWAYS_INLINE HotkeySettingsWidget* getHotkeySettingsWidget() const { return m_hotkey_settings; }
|
||||
|
||||
ALWAYS_INLINE const std::vector<std::pair<std::string, std::string>>& getDeviceList() const { return m_device_list; }
|
||||
ALWAYS_INLINE const QStringList& getVibrationMotors() const { return m_vibration_motors; }
|
||||
|
||||
ALWAYS_INLINE bool isEditingGlobalSettings() const { return m_profile_name.isEmpty(); }
|
||||
ALWAYS_INLINE bool isEditingGlobalSettings() const
|
||||
{
|
||||
return (m_profile_name.isEmpty() && !m_editing_settings_interface);
|
||||
}
|
||||
ALWAYS_INLINE bool isEditingGameSettings() const
|
||||
{
|
||||
return (m_profile_name.isEmpty() && m_editing_settings_interface);
|
||||
}
|
||||
ALWAYS_INLINE bool isEditingProfile() const { return !m_profile_name.isEmpty(); }
|
||||
ALWAYS_INLINE SettingsInterface* getProfileSettingsInterface() { return m_profile_interface.get(); }
|
||||
ALWAYS_INLINE SettingsInterface* getEditingSettingsInterface() { return m_editing_settings_interface; }
|
||||
|
||||
Category getCurrentCategory() const;
|
||||
|
||||
void updateListDescription(u32 global_slot, ControllerBindingWidget* widget);
|
||||
|
||||
void switchProfile(const std::string_view name);
|
||||
|
||||
// Helper functions for updating setting values globally or in the profile.
|
||||
bool getBoolValue(const char* section, const char* key, bool default_value) const;
|
||||
s32 getIntValue(const char* section, const char* key, s32 default_value) const;
|
||||
|
@ -71,6 +84,7 @@ public:
|
|||
void saveAndReloadGameSettings();
|
||||
|
||||
Q_SIGNALS:
|
||||
void windowClosed();
|
||||
void inputProfileSwitched();
|
||||
|
||||
public Q_SLOTS:
|
||||
|
@ -83,6 +97,8 @@ private Q_SLOTS:
|
|||
void onApplyProfileClicked();
|
||||
void onDeleteProfileClicked();
|
||||
void onRestoreDefaultsClicked();
|
||||
void onCopyGlobalSettingsClicked();
|
||||
void onRestoreDefaultsForGameClicked();
|
||||
|
||||
void onInputDevicesEnumerated(const std::vector<std::pair<std::string, std::string>>& devices);
|
||||
void onInputDeviceConnected(const std::string& identifier, const std::string& device_name);
|
||||
|
@ -91,15 +107,19 @@ private Q_SLOTS:
|
|||
|
||||
void createWidgets();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent* event) override;
|
||||
|
||||
private:
|
||||
int getHotkeyCategoryIndex() const;
|
||||
void refreshProfileList();
|
||||
void switchProfile(const QString& name);
|
||||
|
||||
std::array<bool, 2> getEnabledMultitaps() const;
|
||||
|
||||
Ui::ControllerSettingsWindow m_ui;
|
||||
|
||||
SettingsInterface* m_editing_settings_interface = nullptr;
|
||||
|
||||
ControllerGlobalSettingsWidget* m_global_settings = nullptr;
|
||||
std::array<ControllerBindingWidget*, MAX_PORTS> m_port_bindings{};
|
||||
HotkeySettingsWidget* m_hotkey_settings = nullptr;
|
||||
|
@ -108,5 +128,5 @@ private:
|
|||
QStringList m_vibration_motors;
|
||||
|
||||
QString m_profile_name;
|
||||
std::unique_ptr<SettingsInterface> m_profile_interface;
|
||||
std::unique_ptr<SettingsInterface> m_profile_settings_interface;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1279</width>
|
||||
<width>1284</width>
|
||||
<height>672</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<string>DuckStation Controller Settings</string>
|
||||
</property>
|
||||
<property name="windowIcon">
|
||||
<iconset resource="resources/resources.qrc">
|
||||
<iconset resource="resources/duckstation-qt.qrc">
|
||||
<normaloff>:/icons/duck.png</normaloff>:/icons/duck.png</iconset>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
|
@ -65,9 +65,9 @@
|
|||
<item row="1" column="0" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QHBoxLayout" name="editProfileLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<widget class="QLabel" name="editProfileLabel">
|
||||
<property name="text">
|
||||
<string>Editing Profile:</string>
|
||||
</property>
|
||||
|
@ -106,6 +106,16 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="copyGlobalSettings">
|
||||
<property name="text">
|
||||
<string>Copy Global Settings</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="folder-open-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restoreDefaults">
|
||||
<property name="text">
|
||||
|
@ -130,7 +140,7 @@
|
|||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="resources/resources.qrc"/>
|
||||
<include location="resources/duckstation-qt.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "core/game_database.h"
|
||||
#include "core/game_list.h"
|
||||
|
||||
#include "common/error.h"
|
||||
#include "common/string_util.h"
|
||||
|
||||
#include "fmt/format.h"
|
||||
|
@ -54,6 +55,7 @@ GameSummaryWidget::GameSummaryWidget(const std::string& path, const std::string&
|
|||
|
||||
connect(m_ui.compatibilityComments, &QToolButton::clicked, this, &GameSummaryWidget::onCompatibilityCommentsClicked);
|
||||
connect(m_ui.inputProfile, &QComboBox::currentIndexChanged, this, &GameSummaryWidget::onInputProfileChanged);
|
||||
connect(m_ui.editInputProfile, &QAbstractButton::clicked, this, &GameSummaryWidget::onEditInputProfileClicked);
|
||||
connect(m_ui.computeHashes, &QAbstractButton::clicked, this, &GameSummaryWidget::onComputeHashClicked);
|
||||
|
||||
connect(m_ui.title, &QLineEdit::editingFinished, this, [this]() {
|
||||
|
@ -159,15 +161,27 @@ void GameSummaryWidget::populateUi(const std::string& path, const std::string& s
|
|||
|
||||
m_ui.compatibilityComments->setVisible(!m_compatibility_comments.isEmpty());
|
||||
|
||||
m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("controller-digital-line")), tr("Use Global Settings"));
|
||||
m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("global-line")), tr("Use Global Settings"));
|
||||
m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("controller-digital-line")),
|
||||
tr("Game Specific Configuration"));
|
||||
for (const std::string& name : InputManager::GetInputProfileNames())
|
||||
m_ui.inputProfile->addItem(QString::fromStdString(name));
|
||||
|
||||
std::optional<std::string> profile(m_dialog->getStringValue("ControllerPorts", "InputProfileName", std::nullopt));
|
||||
if (profile.has_value())
|
||||
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile.value())));
|
||||
if (m_dialog->getBoolValue("ControllerPorts", "UseGameSettingsForController", std::nullopt).value_or(false))
|
||||
{
|
||||
m_ui.inputProfile->setCurrentIndex(1);
|
||||
}
|
||||
else if (const std::optional<std::string> profile_name =
|
||||
m_dialog->getStringValue("ControllerPorts", "InputProfileName", std::nullopt);
|
||||
profile_name.has_value() && !profile_name->empty())
|
||||
{
|
||||
m_ui.inputProfile->setCurrentIndex(m_ui.inputProfile->findText(QString::fromStdString(profile_name.value())));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ui.inputProfile->setCurrentIndex(0);
|
||||
}
|
||||
m_ui.editInputProfile->setEnabled(m_ui.inputProfile->currentIndex() >= 1);
|
||||
|
||||
populateCustomAttributes();
|
||||
populateTracksInfo();
|
||||
|
@ -221,6 +235,21 @@ void GameSummaryWidget::setCustomRegion(int region)
|
|||
g_main_window->refreshGameListModel();
|
||||
}
|
||||
|
||||
void GameSummaryWidget::setRevisionText(const QString& text)
|
||||
{
|
||||
if (text.isEmpty())
|
||||
return;
|
||||
|
||||
if (m_ui.verifySpacer)
|
||||
{
|
||||
m_ui.verifyLayout->removeItem(m_ui.verifySpacer);
|
||||
delete m_ui.verifySpacer;
|
||||
m_ui.verifySpacer = nullptr;
|
||||
}
|
||||
m_ui.revision->setText(text);
|
||||
m_ui.revision->setVisible(true);
|
||||
}
|
||||
|
||||
static QString MSFTotString(const CDImage::Position& position)
|
||||
{
|
||||
return QStringLiteral("%1:%2:%3 (LBA %4)")
|
||||
|
@ -242,6 +271,11 @@ void GameSummaryWidget::populateTracksInfo()
|
|||
if (!image)
|
||||
return;
|
||||
|
||||
setRevisionText(tr("%1 tracks covering %2 MB (%3 MB on disk)")
|
||||
.arg(image->GetTrackCount())
|
||||
.arg(((image->GetLBACount() * CDImage::RAW_SECTOR_SIZE) + 1048575) / 1048576)
|
||||
.arg((image->GetSizeOnDisk() + 1048575) / 1048576));
|
||||
|
||||
const u32 num_tracks = image->GetTrackCount();
|
||||
for (u32 track = 1; track <= num_tracks; track++)
|
||||
{
|
||||
|
@ -288,10 +322,61 @@ void GameSummaryWidget::onCompatibilityCommentsClicked()
|
|||
|
||||
void GameSummaryWidget::onInputProfileChanged(int index)
|
||||
{
|
||||
|
||||
SettingsInterface* sif = m_dialog->getSettingsInterface();
|
||||
if (index == 0)
|
||||
m_dialog->setStringSettingValue("ControllerPorts", "InputProfileName", std::nullopt);
|
||||
{
|
||||
// Use global settings.
|
||||
sif->DeleteValue("ControllerPorts", "InputProfileName");
|
||||
sif->DeleteValue("ControllerPorts", "UseGameSettingsForController");
|
||||
}
|
||||
else if (index == 1)
|
||||
{
|
||||
// Per-game configuration.
|
||||
sif->DeleteValue("ControllerPorts", "InputProfileName");
|
||||
sif->SetBoolValue("ControllerPorts", "UseGameSettingsForController", true);
|
||||
|
||||
if (!sif->GetBoolValue("ControllerPorts", "GameSettingsInitialized", false))
|
||||
{
|
||||
sif->SetBoolValue("ControllerPorts", "GameSettingsInitialized", true);
|
||||
|
||||
{
|
||||
const auto lock = Host::GetSettingsLock();
|
||||
SettingsInterface* base_sif = Host::Internal::GetBaseSettingsLayer();
|
||||
InputManager::CopyConfiguration(sif, *base_sif, true, true, false);
|
||||
|
||||
QWidget* dlg_parent = QtUtils::GetRootWidget(this);
|
||||
QMessageBox::information(dlg_parent, dlg_parent->windowTitle(),
|
||||
tr("Per-game controller configuration initialized with global settings."));
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
m_dialog->setStringSettingValue("ControllerPorts", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8());
|
||||
{
|
||||
// Input profile.
|
||||
sif->SetStringValue("ControllerPorts", "InputProfileName", m_ui.inputProfile->itemText(index).toUtf8());
|
||||
sif->DeleteValue("ControllerPorts", "UseGameSettingsForController");
|
||||
}
|
||||
|
||||
m_dialog->saveAndReloadGameSettings();
|
||||
m_ui.editInputProfile->setEnabled(index > 0);
|
||||
}
|
||||
|
||||
void GameSummaryWidget::onEditInputProfileClicked()
|
||||
{
|
||||
if (m_dialog->getBoolValue("ControllerPorts", "UseGameSettingsForController", std::nullopt).value_or(false))
|
||||
{
|
||||
// Edit game configuration.
|
||||
ControllerSettingsWindow::editControllerSettingsForGame(QtUtils::GetRootWidget(this),
|
||||
m_dialog->getSettingsInterface());
|
||||
}
|
||||
else if (const std::optional<std::string> profile_name =
|
||||
m_dialog->getStringValue("ControllerPorts", "InputProfileName", std::nullopt);
|
||||
profile_name.has_value() && !profile_name->empty())
|
||||
{
|
||||
// Edit input profile.
|
||||
g_main_window->openInputProfileEditor(profile_name.value());
|
||||
}
|
||||
}
|
||||
|
||||
void GameSummaryWidget::onComputeHashClicked()
|
||||
|
@ -417,17 +502,7 @@ void GameSummaryWidget::onComputeHashClicked()
|
|||
text = mismatch_str;
|
||||
}
|
||||
|
||||
if (!text.isEmpty())
|
||||
{
|
||||
if (m_ui.verifySpacer)
|
||||
{
|
||||
m_ui.verifyLayout->removeItem(m_ui.verifySpacer);
|
||||
delete m_ui.verifySpacer;
|
||||
m_ui.verifySpacer = nullptr;
|
||||
}
|
||||
m_ui.revision->setText(text);
|
||||
m_ui.revision->setVisible(true);
|
||||
}
|
||||
setRevisionText(text);
|
||||
}
|
||||
|
||||
for (u8 track = 0; track < image->GetTrackCount(); track++)
|
||||
|
|
|
@ -27,6 +27,7 @@ public:
|
|||
private Q_SLOTS:
|
||||
void onCompatibilityCommentsClicked();
|
||||
void onInputProfileChanged(int index);
|
||||
void onEditInputProfileClicked();
|
||||
void onComputeHashClicked();
|
||||
|
||||
private:
|
||||
|
@ -36,6 +37,7 @@ private:
|
|||
void updateWindowTitle();
|
||||
void setCustomTitle(const std::string& text);
|
||||
void setCustomRegion(int region);
|
||||
void setRevisionText(const QString& text);
|
||||
|
||||
void populateTracksInfo();
|
||||
|
||||
|
|
|
@ -60,17 +60,17 @@
|
|||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QComboBox" name="region">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="region">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="restoreRegion">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
|
@ -79,7 +79,7 @@
|
|||
<string>Restore</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
|
@ -106,7 +106,7 @@
|
|||
<item row="18" column="0" colspan="2">
|
||||
<widget class="QTableWidget" name="tracks">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
<set>QAbstractItemView::EditTrigger::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="cornerButtonEnabled">
|
||||
<bool>false</bool>
|
||||
|
@ -216,15 +216,12 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<widget class="QComboBox" name="inputProfile"/>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<layout class="QHBoxLayout" name="verifyLayout" stretch="0,1,0">
|
||||
<item>
|
||||
<spacer name="verifySpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
<enum>Qt::Orientation::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
|
@ -306,8 +303,21 @@
|
|||
<string>Comments</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset theme="information-line">
|
||||
<normaloff>.</normaloff>.</iconset>
|
||||
<iconset theme="information-line"/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="13" column="1">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,0">
|
||||
<item>
|
||||
<widget class="QComboBox" name="inputProfile"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="editInputProfile">
|
||||
<property name="text">
|
||||
<string>Edit...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -73,7 +73,7 @@ void HotkeySettingsWidget::createButtons()
|
|||
QLabel* label = new QLabel(qApp->translate("Hotkeys", hotkey->display_name), m_container);
|
||||
layout->addWidget(label, target_row, 0);
|
||||
|
||||
InputBindingWidget* bind = new InputBindingWidget(m_container, m_dialog->getProfileSettingsInterface(),
|
||||
InputBindingWidget* bind = new InputBindingWidget(m_container, m_dialog->getEditingSettingsInterface(),
|
||||
InputBindingInfo::Type::Button, "Hotkeys", hotkey->name);
|
||||
bind->setMinimumWidth(300);
|
||||
layout->addWidget(bind, target_row, 1);
|
||||
|
|
|
@ -2394,6 +2394,13 @@ void MainWindow::doControllerSettings(
|
|||
dlg->setCategory(category);
|
||||
}
|
||||
|
||||
void MainWindow::openInputProfileEditor(const std::string_view name)
|
||||
{
|
||||
ControllerSettingsWindow* dlg = getControllerSettingsWindow();
|
||||
QtUtils::ShowOrRaiseWindow(dlg);
|
||||
dlg->switchProfile(name);
|
||||
}
|
||||
|
||||
void MainWindow::updateDebugMenuCPUExecutionMode()
|
||||
{
|
||||
std::optional<CPUExecutionMode> current_mode =
|
||||
|
|
|
@ -99,6 +99,9 @@ public:
|
|||
/// Accessors for child windows.
|
||||
CheatManagerWindow* getCheatManagerWindow() const { return m_cheat_manager_window; }
|
||||
|
||||
/// Opens the editor for a specific input profile.
|
||||
void openInputProfileEditor(const std::string_view name);
|
||||
|
||||
public Q_SLOTS:
|
||||
/// Updates debug menu visibility (hides if disabled).
|
||||
void updateDebugMenuVisibility();
|
||||
|
|
|
@ -296,7 +296,7 @@ void SettingsWindow::onCopyGlobalSettingsClicked()
|
|||
{
|
||||
auto lock = Host::GetSettingsLock();
|
||||
Settings temp;
|
||||
temp.Load(*Host::Internal::GetBaseSettingsLayer());
|
||||
temp.Load(*Host::Internal::GetBaseSettingsLayer(), *Host::Internal::GetBaseSettingsLayer());
|
||||
temp.Save(*m_sif.get(), true);
|
||||
}
|
||||
saveAndReloadGameSettings();
|
||||
|
|
|
@ -93,6 +93,7 @@ public:
|
|||
bool containsSettingValue(const char* section, const char* key) const;
|
||||
void removeSettingValue(const char* section, const char* key);
|
||||
void saveAndReloadGameSettings();
|
||||
void reloadGameSettingsFromIni();
|
||||
|
||||
bool hasGameTrait(GameDatabase::Trait trait);
|
||||
|
||||
|
|
|
@ -74,16 +74,23 @@ INISettingsInterface::~INISettingsInterface()
|
|||
Save();
|
||||
}
|
||||
|
||||
bool INISettingsInterface::Load()
|
||||
bool INISettingsInterface::Load(Error* error /* = nullptr */)
|
||||
{
|
||||
if (m_filename.empty())
|
||||
{
|
||||
Error::SetStringView(error, "Filename is not set.");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_lock lock(s_ini_load_save_mutex);
|
||||
SI_Error err = SI_FAIL;
|
||||
auto fp = FileSystem::OpenManagedCFile(m_filename.c_str(), "rb");
|
||||
auto fp = FileSystem::OpenManagedCFile(m_filename.c_str(), "rb", error);
|
||||
if (fp)
|
||||
{
|
||||
err = m_ini.LoadFile(fp.get());
|
||||
if (err != SI_OK)
|
||||
Error::SetStringFmt(error, "INI LoadFile() failed: {}", static_cast<int>(err));
|
||||
}
|
||||
|
||||
return (err == SI_OK);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ public:
|
|||
|
||||
const std::string& GetFileName() const { return m_filename; }
|
||||
|
||||
bool Load();
|
||||
bool Load(Error* error = nullptr);
|
||||
bool Save(Error* error = nullptr) override;
|
||||
|
||||
void Clear() override;
|
||||
|
|
|
@ -1809,8 +1809,7 @@ bool InputManager::DoEventHook(InputBindingKey key, float value)
|
|||
// Binding Updater
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si,
|
||||
SettingsInterface& hotkey_binding_si)
|
||||
void InputManager::ReloadBindings(SettingsInterface& binding_si, SettingsInterface& hotkey_binding_si)
|
||||
{
|
||||
PauseVibration();
|
||||
|
||||
|
@ -1843,8 +1842,8 @@ void InputManager::ReloadBindings(SettingsInterface& si, SettingsInterface& bind
|
|||
// From lilypad: 1 mouse pixel = 1/8th way down.
|
||||
const float default_scale = (axis <= static_cast<u32>(InputPointerAxis::Y)) ? 8.0f : 1.0f;
|
||||
s_pointer_axis_scale[axis] =
|
||||
1.0f / std::max(si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(),
|
||||
default_scale),
|
||||
1.0f / std::max(binding_si.GetFloatValue("Pad", fmt::format("Pointer{}Scale", s_pointer_axis_names[axis]).c_str(),
|
||||
default_scale),
|
||||
1.0f);
|
||||
}
|
||||
|
||||
|
|
|
@ -253,7 +253,7 @@ GenericInputBindingMapping GetGenericBindingMapping(std::string_view device);
|
|||
bool IsInputSourceEnabled(SettingsInterface& si, InputSourceType type);
|
||||
|
||||
/// Re-parses the config and registers all hotkey and pad bindings.
|
||||
void ReloadBindings(SettingsInterface& si, SettingsInterface& binding_si, SettingsInterface& hotkey_binding_si);
|
||||
void ReloadBindings(SettingsInterface& si, SettingsInterface& hotkey_binding_si);
|
||||
|
||||
/// Re-parses the sources part of the config and initializes any backends.
|
||||
void ReloadSources(SettingsInterface& si, std::unique_lock<std::mutex>& settings_lock);
|
||||
|
|
Loading…
Reference in New Issue