o add bsnes
This commit is contained in:
183
bsnes/ui_qt/settings/advanced.cpp
Executable file
183
bsnes/ui_qt/settings/advanced.cpp
Executable file
@@ -0,0 +1,183 @@
|
||||
void AdvancedSettingsWindow::setup() {
|
||||
panel = new QWidget;
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Advanced Configuration Settings");
|
||||
title->setProperty("class", "title");
|
||||
layout->addWidget(title);
|
||||
|
||||
driverLayout = new QGridLayout;
|
||||
driverLayout->setVerticalSpacing(0);
|
||||
driverLayout->setHorizontalSpacing(Style::WidgetSpacing); {
|
||||
videoLabel = new QLabel("Video driver:");
|
||||
driverLayout->addWidget(videoLabel, 0, 0);
|
||||
|
||||
audioLabel = new QLabel("Audio driver:");
|
||||
driverLayout->addWidget(audioLabel, 0, 1);
|
||||
|
||||
inputLabel = new QLabel("Input driver:");
|
||||
driverLayout->addWidget(inputLabel, 0, 2);
|
||||
|
||||
videoDriver = new QComboBox;
|
||||
driverLayout->addWidget(videoDriver, 1, 0);
|
||||
|
||||
audioDriver = new QComboBox;
|
||||
driverLayout->addWidget(audioDriver, 1, 1);
|
||||
|
||||
inputDriver = new QComboBox;
|
||||
driverLayout->addWidget(inputDriver, 1, 2);
|
||||
|
||||
driverInfo = new QLabel("<i>Note: driver changes require restart to take effect.</i>");
|
||||
driverLayout->addWidget(driverInfo, 2, 0, 1, 3);
|
||||
}
|
||||
layout->addLayout(driverLayout);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
regionTitle = new QLabel("Hardware region:");
|
||||
layout->addWidget(regionTitle);
|
||||
|
||||
regionLayout = new QHBoxLayout;
|
||||
regionLayout->setSpacing(Style::WidgetSpacing); {
|
||||
regionGroup = new QButtonGroup(panel);
|
||||
|
||||
regionAuto = new QRadioButton("Auto-detect");
|
||||
regionAuto->setToolTip("Automatically select hardware region on cartridge load");
|
||||
regionGroup->addButton(regionAuto);
|
||||
regionLayout->addWidget(regionAuto);
|
||||
|
||||
regionNTSC = new QRadioButton("NTSC");
|
||||
regionNTSC->setToolTip("Force NTSC region (Japan, Korea, US)");
|
||||
regionGroup->addButton(regionNTSC);
|
||||
regionLayout->addWidget(regionNTSC);
|
||||
|
||||
regionPAL = new QRadioButton("PAL");
|
||||
regionPAL->setToolTip("Force PAL region (Europe, ...)");
|
||||
regionGroup->addButton(regionPAL);
|
||||
regionLayout->addWidget(regionPAL);
|
||||
}
|
||||
layout->addLayout(regionLayout);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
portTitle = new QLabel("Expansion port device:");
|
||||
layout->addWidget(portTitle);
|
||||
|
||||
portLayout = new QHBoxLayout;
|
||||
portLayout->setSpacing(Style::WidgetSpacing); {
|
||||
portGroup = new QButtonGroup(panel);
|
||||
|
||||
portSatellaview = new QRadioButton("Satellaview");
|
||||
portGroup->addButton(portSatellaview);
|
||||
portLayout->addWidget(portSatellaview);
|
||||
|
||||
portNone = new QRadioButton("None");
|
||||
portGroup->addButton(portNone);
|
||||
portLayout->addWidget(portNone);
|
||||
|
||||
portSpacer = new QWidget;
|
||||
portLayout->addWidget(portSpacer);
|
||||
}
|
||||
layout->addLayout(portLayout);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
focusTitle = new QLabel("When main window does not have focus:");
|
||||
layout->addWidget(focusTitle);
|
||||
|
||||
focusLayout = new QHBoxLayout;
|
||||
focusLayout->setSpacing(Style::WidgetSpacing); {
|
||||
focusButtonGroup = new QButtonGroup(panel);
|
||||
|
||||
focusPause = new QRadioButton("Pause emulation");
|
||||
focusPause->setToolTip("Ideal for prolonged multi-tasking");
|
||||
focusButtonGroup->addButton(focusPause);
|
||||
focusLayout->addWidget(focusPause);
|
||||
|
||||
focusIgnore = new QRadioButton("Ignore input");
|
||||
focusIgnore->setToolTip("Ideal for light multi-tasking when using keyboard");
|
||||
focusButtonGroup->addButton(focusIgnore);
|
||||
focusLayout->addWidget(focusIgnore);
|
||||
|
||||
focusAllow = new QRadioButton("Allow input");
|
||||
focusAllow->setToolTip("Ideal for light multi-tasking when using joypad(s)");
|
||||
focusButtonGroup->addButton(focusAllow);
|
||||
focusLayout->addWidget(focusAllow);
|
||||
}
|
||||
layout->addLayout(focusLayout);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
panel->setLayout(layout);
|
||||
initializeUi();
|
||||
|
||||
connect(videoDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(videoDriverChange(int)));
|
||||
connect(audioDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(audioDriverChange(int)));
|
||||
connect(inputDriver, SIGNAL(currentIndexChanged(int)), this, SLOT(inputDriverChange(int)));
|
||||
connect(regionAuto, SIGNAL(pressed()), this, SLOT(setRegionAuto()));
|
||||
connect(regionNTSC, SIGNAL(pressed()), this, SLOT(setRegionNTSC()));
|
||||
connect(regionPAL, SIGNAL(pressed()), this, SLOT(setRegionPAL()));
|
||||
connect(portSatellaview, SIGNAL(pressed()), this, SLOT(setPortSatellaview()));
|
||||
connect(portNone, SIGNAL(pressed()), this, SLOT(setPortNone()));
|
||||
connect(focusPause, SIGNAL(pressed()), this, SLOT(pauseWithoutFocus()));
|
||||
connect(focusIgnore, SIGNAL(pressed()), this, SLOT(ignoreInputWithoutFocus()));
|
||||
connect(focusAllow, SIGNAL(pressed()), this, SLOT(allowInputWithoutFocus()));
|
||||
}
|
||||
|
||||
void AdvancedSettingsWindow::initializeUi() {
|
||||
lstring part;
|
||||
|
||||
part.split(";", video.driver_list());
|
||||
for(unsigned i = 0; i < part.size(); i++) {
|
||||
videoDriver->addItem(utf8() << part[i]);
|
||||
if(part[i] == config.system.video) videoDriver->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
part.split(";", audio.driver_list());
|
||||
for(unsigned i = 0; i < part.size(); i++) {
|
||||
audioDriver->addItem(utf8() << part[i]);
|
||||
if(part[i] == config.system.audio) audioDriver->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
part.split(";", input.driver_list());
|
||||
for(unsigned i = 0; i < part.size(); i++) {
|
||||
inputDriver->addItem(utf8() << part[i]);
|
||||
if(part[i] == config.system.input) inputDriver->setCurrentIndex(i);
|
||||
}
|
||||
|
||||
regionAuto->setChecked(snes.config.region == SNES::Autodetect);
|
||||
regionNTSC->setChecked(snes.config.region == SNES::NTSC);
|
||||
regionPAL->setChecked (snes.config.region == SNES::PAL);
|
||||
|
||||
portSatellaview->setChecked(snes.config.expansion_port == SNES::ExpansionBSX);
|
||||
portNone->setChecked (snes.config.expansion_port == SNES::ExpansionNone);
|
||||
|
||||
focusPause->setChecked (config.input.focusPolicy == Configuration::Input::FocusPolicyPauseEmulation);
|
||||
focusIgnore->setChecked(config.input.focusPolicy == Configuration::Input::FocusPolicyIgnoreInput);
|
||||
focusAllow->setChecked (config.input.focusPolicy == Configuration::Input::FocusPolicyAllowInput);
|
||||
}
|
||||
|
||||
void AdvancedSettingsWindow::videoDriverChange(int index) {
|
||||
if(index >= 0) config.system.video = videoDriver->itemText(index).toUtf8().data();
|
||||
}
|
||||
|
||||
void AdvancedSettingsWindow::audioDriverChange(int index) {
|
||||
if(index >= 0) config.system.audio = audioDriver->itemText(index).toUtf8().data();
|
||||
}
|
||||
|
||||
void AdvancedSettingsWindow::inputDriverChange(int index) {
|
||||
if(index >= 0) config.system.input = inputDriver->itemText(index).toUtf8().data();
|
||||
}
|
||||
|
||||
void AdvancedSettingsWindow::setRegionAuto() { snes.config.region = SNES::Autodetect; }
|
||||
void AdvancedSettingsWindow::setRegionNTSC() { snes.config.region = SNES::NTSC; }
|
||||
void AdvancedSettingsWindow::setRegionPAL() { snes.config.region = SNES::PAL; }
|
||||
|
||||
void AdvancedSettingsWindow::setPortSatellaview() { snes.config.expansion_port = SNES::ExpansionBSX; }
|
||||
void AdvancedSettingsWindow::setPortNone() { snes.config.expansion_port = SNES::ExpansionNone; }
|
||||
|
||||
void AdvancedSettingsWindow::pauseWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyPauseEmulation; }
|
||||
void AdvancedSettingsWindow::ignoreInputWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyIgnoreInput; }
|
||||
void AdvancedSettingsWindow::allowInputWithoutFocus() { config.input.focusPolicy = Configuration::Input::FocusPolicyAllowInput; }
|
||||
54
bsnes/ui_qt/settings/advanced.hpp
Executable file
54
bsnes/ui_qt/settings/advanced.hpp
Executable file
@@ -0,0 +1,54 @@
|
||||
class AdvancedSettingsWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *panel;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *title;
|
||||
QGridLayout *driverLayout;
|
||||
QLabel *videoLabel;
|
||||
QLabel *audioLabel;
|
||||
QLabel *inputLabel;
|
||||
QComboBox *videoDriver;
|
||||
QComboBox *audioDriver;
|
||||
QComboBox *inputDriver;
|
||||
QLabel *driverInfo;
|
||||
|
||||
QLabel *regionTitle;
|
||||
QHBoxLayout *regionLayout;
|
||||
QButtonGroup *regionGroup;
|
||||
QRadioButton *regionAuto;
|
||||
QRadioButton *regionNTSC;
|
||||
QRadioButton *regionPAL;
|
||||
|
||||
QLabel *portTitle;
|
||||
QHBoxLayout *portLayout;
|
||||
QButtonGroup *portGroup;
|
||||
QRadioButton *portSatellaview;
|
||||
QRadioButton *portNone;
|
||||
QWidget *portSpacer;
|
||||
|
||||
QLabel *focusTitle;
|
||||
QHBoxLayout *focusLayout;
|
||||
QButtonGroup *focusButtonGroup;
|
||||
QRadioButton *focusPause;
|
||||
QRadioButton *focusIgnore;
|
||||
QRadioButton *focusAllow;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void initializeUi();
|
||||
|
||||
public slots:
|
||||
void videoDriverChange(int index);
|
||||
void audioDriverChange(int index);
|
||||
void inputDriverChange(int index);
|
||||
void setRegionAuto();
|
||||
void setRegionNTSC();
|
||||
void setRegionPAL();
|
||||
void setPortSatellaview();
|
||||
void setPortNone();
|
||||
void pauseWithoutFocus();
|
||||
void ignoreInputWithoutFocus();
|
||||
void allowInputWithoutFocus();
|
||||
} *winAdvancedSettings;
|
||||
129
bsnes/ui_qt/settings/audio.cpp
Executable file
129
bsnes/ui_qt/settings/audio.cpp
Executable file
@@ -0,0 +1,129 @@
|
||||
void AudioSettingsWindow::setup() {
|
||||
panel = new QWidget;
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Audio Settings");
|
||||
title->setProperty("class", "title");
|
||||
layout->addWidget(title);
|
||||
|
||||
boxes = new QHBoxLayout; {
|
||||
frequencyLabel = new QLabel("Frequency:");
|
||||
frequencyLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
boxes->addWidget(frequencyLabel);
|
||||
|
||||
frequency = new QComboBox;
|
||||
frequency->addItem("32000hz");
|
||||
frequency->addItem("44100hz");
|
||||
frequency->addItem("48000hz");
|
||||
frequency->addItem("96000hz");
|
||||
boxes->addWidget(frequency);
|
||||
|
||||
latencyLabel = new QLabel("Latency:");
|
||||
latencyLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
boxes->addWidget(latencyLabel);
|
||||
|
||||
latency = new QComboBox;
|
||||
latency->addItem("20ms");
|
||||
latency->addItem("40ms");
|
||||
latency->addItem("60ms");
|
||||
latency->addItem("80ms");
|
||||
latency->addItem("100ms");
|
||||
latency->addItem("120ms");
|
||||
boxes->addWidget(latency);
|
||||
}
|
||||
boxes->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(boxes);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
sliders = new QGridLayout; {
|
||||
volumeLabel = new QLabel("Volume: 100%");
|
||||
volumeLabel->setToolTip("Warning: any volume other than 100% will result in a slight audio quality loss");
|
||||
sliders->addWidget(volumeLabel, 0, 0);
|
||||
|
||||
volume = new QSlider(Qt::Horizontal);
|
||||
volume->setMinimum(0);
|
||||
volume->setMaximum(200);
|
||||
sliders->addWidget(volume, 0, 1);
|
||||
|
||||
frequencySkewLabel = new QLabel("Input frequency: 32000hz");
|
||||
frequencySkewLabel->setToolTip(
|
||||
"Adjusts audio resampling rate.\n"
|
||||
"When both video sync and audio sync are enabled, use this setting to fine-tune the output.\n"
|
||||
"Lower the input frequency to clean audio output, eliminating crackling / popping.\n"
|
||||
"Raise the input frequency to smooth video output, eliminating duplicated frames."
|
||||
);
|
||||
sliders->addWidget(frequencySkewLabel, 1, 0);
|
||||
|
||||
frequencySkew = new QSlider(Qt::Horizontal);
|
||||
frequencySkew->setMinimum(31800);
|
||||
frequencySkew->setMaximum(32200);
|
||||
sliders->addWidget(frequencySkew);
|
||||
}
|
||||
sliders->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(sliders);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
panel->setLayout(layout);
|
||||
connect(frequency, SIGNAL(currentIndexChanged(int)), this, SLOT(frequencyChange(int)));
|
||||
connect(latency, SIGNAL(currentIndexChanged(int)), this, SLOT(latencyChange(int)));
|
||||
connect(volume, SIGNAL(valueChanged(int)), this, SLOT(volumeAdjust(int)));
|
||||
connect(frequencySkew, SIGNAL(valueChanged(int)), this, SLOT(frequencySkewAdjust(int)));
|
||||
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void AudioSettingsWindow::syncUi() {
|
||||
int n;
|
||||
|
||||
n = config.audio.outputFrequency;
|
||||
if(n <= 32000) frequency->setCurrentIndex(0);
|
||||
else if(n <= 44100) frequency->setCurrentIndex(1);
|
||||
else if(n <= 48000) frequency->setCurrentIndex(2);
|
||||
else if(n <= 96000) frequency->setCurrentIndex(3);
|
||||
else frequency->setCurrentIndex(0);
|
||||
|
||||
n = config.audio.latency;
|
||||
latency->setCurrentIndex((n - 20) / 20);
|
||||
|
||||
n = config.audio.volume;
|
||||
volumeLabel->setText(utf8() << "Volume: " << n << "%");
|
||||
volume->setSliderPosition(n);
|
||||
|
||||
n = config.audio.inputFrequency;
|
||||
frequencySkewLabel->setText(utf8() << "Input frequency: " << n << "hz");
|
||||
frequencySkew->setSliderPosition(n);
|
||||
}
|
||||
|
||||
void AudioSettingsWindow::frequencyChange(int value) {
|
||||
switch(value) { default:
|
||||
case 0: config.audio.outputFrequency = 32000; break;
|
||||
case 1: config.audio.outputFrequency = 44100; break;
|
||||
case 2: config.audio.outputFrequency = 48000; break;
|
||||
case 3: config.audio.outputFrequency = 96000; break;
|
||||
}
|
||||
audio.set(Audio::Frequency, config.audio.outputFrequency);
|
||||
}
|
||||
|
||||
void AudioSettingsWindow::latencyChange(int value) {
|
||||
value = max(0, min(5, value));
|
||||
config.audio.latency = 20 + value * 20;
|
||||
audio.set(Audio::Latency, config.audio.latency);
|
||||
}
|
||||
|
||||
void AudioSettingsWindow::volumeAdjust(int value) {
|
||||
config.audio.volume = value;
|
||||
audio.set(Audio::Volume, config.audio.volume);
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void AudioSettingsWindow::frequencySkewAdjust(int value) {
|
||||
config.audio.inputFrequency = value;
|
||||
utility.updateEmulationSpeed();
|
||||
syncUi();
|
||||
}
|
||||
28
bsnes/ui_qt/settings/audio.hpp
Executable file
28
bsnes/ui_qt/settings/audio.hpp
Executable file
@@ -0,0 +1,28 @@
|
||||
class AudioSettingsWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *panel;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *title;
|
||||
QHBoxLayout *boxes;
|
||||
QLabel *frequencyLabel;
|
||||
QComboBox *frequency;
|
||||
QLabel *latencyLabel;
|
||||
QComboBox *latency;
|
||||
QGridLayout *sliders;
|
||||
QLabel *volumeLabel;
|
||||
QSlider *volume;
|
||||
QLabel *frequencySkewLabel;
|
||||
QSlider *frequencySkew;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
|
||||
public slots:
|
||||
void frequencyChange(int value);
|
||||
void latencyChange(int value);
|
||||
void volumeAdjust(int value);
|
||||
void frequencySkewAdjust(int value);
|
||||
} *winAudioSettings;
|
||||
156
bsnes/ui_qt/settings/cheateditor.cpp
Executable file
156
bsnes/ui_qt/settings/cheateditor.cpp
Executable file
@@ -0,0 +1,156 @@
|
||||
void CheatEditorWindow::setup() {
|
||||
panel = new QWidget;
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Cheat Code Editor");
|
||||
title->setProperty("class", "title");
|
||||
layout->addWidget(title);
|
||||
|
||||
list = new QTreeWidget;
|
||||
list->setColumnCount(4);
|
||||
list->setHeaderLabels(QStringList() << "Hidden" << "" << "Code" << "Description");
|
||||
list->sortByColumn(0, Qt::AscendingOrder); //set initial sorting, preserve user setting after reloadList()
|
||||
list->hideColumn(0); //used for default sorting + hides child expansion box
|
||||
layout->addWidget(list);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
controls = new QHBoxLayout; {
|
||||
addCode = new QPushButton("Add Code ...");
|
||||
controls->addWidget(addCode);
|
||||
|
||||
editCode = new QPushButton("Edit Code ...");
|
||||
controls->addWidget(editCode);
|
||||
|
||||
deleteCode = new QPushButton("Delete Code");
|
||||
controls->addWidget(deleteCode);
|
||||
}
|
||||
controls->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(controls);
|
||||
|
||||
panel->setLayout(layout);
|
||||
connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*)));
|
||||
connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(editSelectedCode()));
|
||||
connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged()));
|
||||
connect(addCode, SIGNAL(released()), this, SLOT(addNewCode()));
|
||||
connect(editCode, SIGNAL(released()), this, SLOT(editSelectedCode()));
|
||||
connect(deleteCode, SIGNAL(released()), this, SLOT(deleteSelectedCode()));
|
||||
|
||||
reloadList();
|
||||
}
|
||||
|
||||
void CheatEditorWindow::syncUi() {
|
||||
addCode->setEnabled(cartridge.loaded());
|
||||
QList<QTreeWidgetItem*> itemList = list->selectedItems();
|
||||
editCode->setEnabled(cartridge.loaded() && itemList.count() == 1);
|
||||
deleteCode->setEnabled(cartridge.loaded() && itemList.count() == 1);
|
||||
}
|
||||
|
||||
//called when loading a new game, or after adding / deleting a code:
|
||||
//synchronizes list with cheat class list
|
||||
void CheatEditorWindow::reloadList() {
|
||||
disconnect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*)));
|
||||
|
||||
list->clear();
|
||||
list->setSortingEnabled(false);
|
||||
listItem.reset();
|
||||
|
||||
if(cartridge.loaded()) {
|
||||
for(unsigned i = 0; i < cheat.count(); i++) {
|
||||
Cheat::cheat_t code;
|
||||
cheat.get(i, code);
|
||||
|
||||
//only want to show one code / description line in list
|
||||
lstring lcode, ldesc;
|
||||
lcode.split("+", code.code);
|
||||
ldesc.split("\n", code.desc);
|
||||
if(lcode.size() > 1) lcode[0] << "+" << (int)(lcode.size() - 1);
|
||||
if(ldesc.size() > 1) ldesc[0] << " ...";
|
||||
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(list);
|
||||
item->setText(0, utf8() << (int)(1000000 + i));
|
||||
item->setCheckState(1, code.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setText(2, utf8() << lcode[0]);
|
||||
item->setText(3, utf8() << ldesc[0]);
|
||||
listItem.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
list->setSortingEnabled(true);
|
||||
list->resizeColumnToContents(1); //shrink checkbox column width
|
||||
list->resizeColumnToContents(2); //shrink code column width
|
||||
connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*)));
|
||||
syncUi();
|
||||
}
|
||||
|
||||
//called when modifying an existing code:
|
||||
//list items are all still valid, only need to update item codes + descriptions
|
||||
void CheatEditorWindow::updateList() {
|
||||
disconnect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*)));
|
||||
|
||||
for(unsigned i = 0; i < listItem.size(); i++) {
|
||||
Cheat::cheat_t code;
|
||||
cheat.get(i, code);
|
||||
|
||||
//only want to show one code / description line in list
|
||||
lstring lcode, ldesc;
|
||||
lcode.split("+", code.code);
|
||||
ldesc.split("\n", code.desc);
|
||||
if(lcode.size() > 1) lcode[0] << "+" << (int)(lcode.size() - 1);
|
||||
if(ldesc.size() > 1) ldesc[0] << " ...";
|
||||
|
||||
QTreeWidgetItem *item = listItem[i];
|
||||
item->setCheckState(1, code.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
item->setText(2, utf8() << lcode[0]);
|
||||
item->setText(3, utf8() << ldesc[0]);
|
||||
}
|
||||
|
||||
list->resizeColumnToContents(1); //shrink checkbox column width
|
||||
list->resizeColumnToContents(2); //shrink code column width
|
||||
connect(list, SIGNAL(itemChanged(QTreeWidgetItem*, int)), this, SLOT(itemChanged(QTreeWidgetItem*)));
|
||||
syncUi();
|
||||
}
|
||||
|
||||
//called when item enabled checkbox was clicked (eg cheat code enable state was toggled on or off)
|
||||
void CheatEditorWindow::itemChanged(QTreeWidgetItem *item) {
|
||||
signed i = listItem.find(item);
|
||||
if(i >= 0) item->checkState(1) == Qt::Checked ? cheat.enable(i) : cheat.disable(i);
|
||||
}
|
||||
|
||||
void CheatEditorWindow::listChanged() {
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void CheatEditorWindow::addNewCode() {
|
||||
if(cartridge.loaded()) winCodeEditor->addCode();
|
||||
}
|
||||
|
||||
void CheatEditorWindow::editSelectedCode() {
|
||||
if(cartridge.loaded()) {
|
||||
QTreeWidgetItem *item = list->currentItem();
|
||||
if(item && item->isSelected()) {
|
||||
signed i = listItem.find(item);
|
||||
if(i >= 0) winCodeEditor->editCode(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CheatEditorWindow::deleteSelectedCode() {
|
||||
if(cartridge.loaded()) {
|
||||
QTreeWidgetItem *item = list->currentItem();
|
||||
if(item && item->isSelected()) {
|
||||
signed i = listItem.find(item);
|
||||
if(i >= 0) {
|
||||
//if code editor is modifying an existing code, its index will be invalidated by delete;
|
||||
//therefore, the editor window must be closed first
|
||||
if(winCodeEditor->activeCode >= 0) winCodeEditor->dismiss();
|
||||
|
||||
//remove code, and resync listItem with cheat list
|
||||
cheat.remove(i);
|
||||
reloadList();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
28
bsnes/ui_qt/settings/cheateditor.hpp
Executable file
28
bsnes/ui_qt/settings/cheateditor.hpp
Executable file
@@ -0,0 +1,28 @@
|
||||
class CheatEditorWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *panel;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *title;
|
||||
QTreeWidget *list;
|
||||
QHBoxLayout *controls;
|
||||
QPushButton *addCode;
|
||||
QPushButton *editCode;
|
||||
QPushButton *deleteCode;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
void reloadList();
|
||||
void updateList();
|
||||
|
||||
public slots:
|
||||
void itemChanged(QTreeWidgetItem *item);
|
||||
void listChanged();
|
||||
void addNewCode();
|
||||
void editSelectedCode();
|
||||
void deleteSelectedCode();
|
||||
|
||||
private:
|
||||
array<QTreeWidgetItem*> listItem;
|
||||
} *winCheatEditor;
|
||||
174
bsnes/ui_qt/settings/input.cpp
Executable file
174
bsnes/ui_qt/settings/input.cpp
Executable file
@@ -0,0 +1,174 @@
|
||||
void InputSettingsWindow::setup() {
|
||||
panel = new QWidget;
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Input Configuration Editor");
|
||||
title->setProperty("class", "title");
|
||||
layout->addWidget(title);
|
||||
|
||||
selection = new QHBoxLayout; {
|
||||
port = new QComboBox;
|
||||
port->addItem("Controller Port 1");
|
||||
port->addItem("Controller Port 2");
|
||||
port->addItem("User Interface");
|
||||
selection->addWidget(port);
|
||||
|
||||
device = new QComboBox;
|
||||
selection->addWidget(device);
|
||||
}
|
||||
selection->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(selection);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
list = new QTreeWidget;
|
||||
list->setColumnCount(3);
|
||||
list->setHeaderLabels(QStringList() << "Hidden" << "Name" << "Assignment");
|
||||
list->hideColumn(0); //used for default sorting + hides child expansion box
|
||||
layout->addWidget(list);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
controls = new QHBoxLayout; {
|
||||
assign = new QPushButton("Assign ...");
|
||||
controls->addWidget(assign);
|
||||
|
||||
assignAll = new QPushButton("Assign All ...");
|
||||
controls->addWidget(assignAll);
|
||||
|
||||
unassign = new QPushButton("Unassign");
|
||||
controls->addWidget(unassign);
|
||||
}
|
||||
controls->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(controls);
|
||||
|
||||
panel->setLayout(layout);
|
||||
connect(port, SIGNAL(currentIndexChanged(int)), this, SLOT(portChanged()));
|
||||
connect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList()));
|
||||
connect(list, SIGNAL(itemActivated(QTreeWidgetItem*, int)), this, SLOT(assignKey()));
|
||||
connect(list, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged()));
|
||||
connect(assign, SIGNAL(released()), this, SLOT(assignKey()));
|
||||
connect(assignAll, SIGNAL(released()), this, SLOT(assignAllKeys()));
|
||||
connect(unassign, SIGNAL(released()), this, SLOT(unassignKey()));
|
||||
|
||||
portChanged();
|
||||
}
|
||||
|
||||
void InputSettingsWindow::syncUi() {
|
||||
QList<QTreeWidgetItem*> itemList = list->selectedItems();
|
||||
assign->setEnabled(itemList.count() == 1);
|
||||
//allow rapid assign for controllers from both ports, but not for UI shortcuts
|
||||
assignAll->setEnabled(port->currentIndex() < 2);
|
||||
unassign->setEnabled(itemList.count() == 1);
|
||||
}
|
||||
|
||||
//when port combobox item is changed, device list needs to be repopulated
|
||||
void InputSettingsWindow::portChanged() {
|
||||
disconnect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList()));
|
||||
|
||||
device->clear();
|
||||
deviceItem.reset();
|
||||
|
||||
int index = port->currentIndex();
|
||||
if(index < 2) {
|
||||
//this is a controller port
|
||||
for(unsigned i = 0; i < inputPool.size(); i++) {
|
||||
//only add devices for selected port
|
||||
if(inputPool[i]->port == index) {
|
||||
device->addItem(inputPool[i]->name);
|
||||
deviceItem.add(inputPool[i]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//user interface controls
|
||||
for(unsigned i = 0; i < inputUiPool.size(); i++) {
|
||||
device->addItem(inputUiPool[i]->name);
|
||||
deviceItem.add(inputUiPool[i]);
|
||||
}
|
||||
}
|
||||
|
||||
reloadList();
|
||||
connect(device, SIGNAL(currentIndexChanged(int)), this, SLOT(reloadList()));
|
||||
}
|
||||
|
||||
//when device combobox item is changed, object list needs to be repopulated
|
||||
void InputSettingsWindow::reloadList() {
|
||||
list->clear();
|
||||
list->setSortingEnabled(false);
|
||||
listItem.reset();
|
||||
|
||||
int index = device->currentIndex();
|
||||
if(index < deviceItem.size()) {
|
||||
InputGroup &group = *deviceItem[index];
|
||||
for(unsigned i = 0; i < group.size(); i++) {
|
||||
QTreeWidgetItem *item = new QTreeWidgetItem(list);
|
||||
item->setText(0, utf8() << (int)(1000000 + i));
|
||||
item->setText(1, group[i]->name);
|
||||
item->setText(2, (const char*)group[i]->id);
|
||||
listItem.add(item);
|
||||
}
|
||||
}
|
||||
|
||||
list->setSortingEnabled(true);
|
||||
list->sortByColumn(0, Qt::AscendingOrder); //set default sorting on list change, overriding user setting
|
||||
list->resizeColumnToContents(1); //shrink name column
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void InputSettingsWindow::listChanged() {
|
||||
syncUi();
|
||||
}
|
||||
|
||||
//InputCaptureWindow calls this after a successful key assignment change:
|
||||
//need to update list of values to show new key assignment value.
|
||||
void InputSettingsWindow::updateList() {
|
||||
int index = device->currentIndex();
|
||||
if(index < deviceItem.size()) {
|
||||
InputGroup &group = *deviceItem[index];
|
||||
|
||||
for(unsigned i = 0; i < listItem.size(); i++) {
|
||||
listItem[i]->setText(2, (const char*)group[i]->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettingsWindow::assignKey() {
|
||||
int index = device->currentIndex();
|
||||
if(index < deviceItem.size()) {
|
||||
InputGroup &group = *deviceItem[index];
|
||||
|
||||
QTreeWidgetItem *item = list->currentItem();
|
||||
if(item && item->isSelected()) {
|
||||
signed i = listItem.find(item);
|
||||
if(i >= 0) winInputCapture->activate(group[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettingsWindow::assignAllKeys() {
|
||||
int index = port->currentIndex();
|
||||
if(index < 2) {
|
||||
index = device->currentIndex();
|
||||
if(index < deviceItem.size()) {
|
||||
winInputCapture->activate(deviceItem[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputSettingsWindow::unassignKey() {
|
||||
int index = device->currentIndex();
|
||||
if(index < deviceItem.size()) {
|
||||
InputGroup &group = *deviceItem[index];
|
||||
|
||||
QTreeWidgetItem *item = list->currentItem();
|
||||
if(item && item->isSelected()) {
|
||||
signed i = listItem.find(item);
|
||||
if(i >= 0) {
|
||||
group[i]->id = "none";
|
||||
inputManager.bind(); //update key bindings to reflect object ID change above
|
||||
item->setText(2, (const char*)group[i]->id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
32
bsnes/ui_qt/settings/input.hpp
Executable file
32
bsnes/ui_qt/settings/input.hpp
Executable file
@@ -0,0 +1,32 @@
|
||||
class InputSettingsWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *panel;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *title;
|
||||
QHBoxLayout *selection;
|
||||
QComboBox *port;
|
||||
QComboBox *device;
|
||||
QTreeWidget *list;
|
||||
QHBoxLayout *controls;
|
||||
QPushButton *assign;
|
||||
QPushButton *assignAll;
|
||||
QPushButton *unassign;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
|
||||
public slots:
|
||||
void portChanged();
|
||||
void reloadList();
|
||||
void listChanged();
|
||||
void updateList();
|
||||
void assignKey();
|
||||
void assignAllKeys();
|
||||
void unassignKey();
|
||||
|
||||
private:
|
||||
array<InputGroup*> deviceItem;
|
||||
array<QTreeWidgetItem*> listItem;
|
||||
} *winInputSettings;
|
||||
200
bsnes/ui_qt/settings/paths.cpp
Executable file
200
bsnes/ui_qt/settings/paths.cpp
Executable file
@@ -0,0 +1,200 @@
|
||||
void PathSettingsWindow::setup() {
|
||||
panel = new QWidget;
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Default Folder Paths");
|
||||
title->setProperty("class", "title");
|
||||
layout->addWidget(title);
|
||||
|
||||
gameLabel = new QLabel("Games:");
|
||||
layout->addWidget(gameLabel);
|
||||
|
||||
games = new QHBoxLayout; {
|
||||
games->setMargin(0);
|
||||
|
||||
gamePath = new QLineEdit;
|
||||
gamePath->setReadOnly(true);
|
||||
games->addWidget(gamePath);
|
||||
|
||||
gameSelect = new QPushButton("Select ...");
|
||||
games->addWidget(gameSelect);
|
||||
|
||||
gameDefault = new QPushButton("Default");
|
||||
games->addWidget(gameDefault);
|
||||
}
|
||||
games->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(games);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
saveLabel = new QLabel("Save RAM:");
|
||||
layout->addWidget(saveLabel);
|
||||
|
||||
saves = new QHBoxLayout; {
|
||||
saves->setMargin(0);
|
||||
|
||||
savePath = new QLineEdit;
|
||||
savePath->setReadOnly(true);
|
||||
saves->addWidget(savePath);
|
||||
|
||||
saveSelect = new QPushButton("Select ...");
|
||||
saves->addWidget(saveSelect);
|
||||
|
||||
saveDefault = new QPushButton("Default");
|
||||
saves->addWidget(saveDefault);
|
||||
}
|
||||
saves->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(saves);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
patchLabel = new QLabel("UPS patches:");
|
||||
layout->addWidget(patchLabel);
|
||||
|
||||
patches = new QHBoxLayout; {
|
||||
patches->setMargin(0);
|
||||
|
||||
patchPath = new QLineEdit;
|
||||
patchPath->setReadOnly(true);
|
||||
patches->addWidget(patchPath);
|
||||
|
||||
patchSelect = new QPushButton("Select ...");
|
||||
patches->addWidget(patchSelect);
|
||||
|
||||
patchDefault = new QPushButton("Default");
|
||||
patches->addWidget(patchDefault);
|
||||
}
|
||||
patches->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(patches);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
cheatLabel = new QLabel("Cheat codes:");
|
||||
layout->addWidget(cheatLabel);
|
||||
|
||||
cheats = new QHBoxLayout; {
|
||||
cheats->setMargin(0);
|
||||
|
||||
cheatPath = new QLineEdit;
|
||||
cheatPath->setReadOnly(true);
|
||||
cheats->addWidget(cheatPath);
|
||||
|
||||
cheatSelect = new QPushButton("Select ...");
|
||||
cheats->addWidget(cheatSelect);
|
||||
|
||||
cheatDefault = new QPushButton("Default");
|
||||
cheats->addWidget(cheatDefault);
|
||||
}
|
||||
cheats->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(cheats);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
dataLabel = new QLabel("Export data:");
|
||||
layout->addWidget(dataLabel);
|
||||
|
||||
data = new QHBoxLayout;
|
||||
data->setMargin(0);
|
||||
data->setSpacing(Style::WidgetSpacing); {
|
||||
dataPath = new QLineEdit;
|
||||
dataPath->setReadOnly(true);
|
||||
data->addWidget(dataPath);
|
||||
|
||||
dataSelect = new QPushButton("Select ...");
|
||||
data->addWidget(dataSelect);
|
||||
|
||||
dataDefault = new QPushButton("Default");
|
||||
data->addWidget(dataDefault);
|
||||
}
|
||||
layout->addLayout(data);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
syncUi();
|
||||
|
||||
panel->setLayout(layout);
|
||||
connect(gameSelect, SIGNAL(released()), this, SLOT(selectGamePath()));
|
||||
connect(gameDefault, SIGNAL(released()), this, SLOT(defaultGamePath()));
|
||||
connect(saveSelect, SIGNAL(released()), this, SLOT(selectSavePath()));
|
||||
connect(saveDefault, SIGNAL(released()), this, SLOT(defaultSavePath()));
|
||||
connect(patchSelect, SIGNAL(released()), this, SLOT(selectPatchPath()));
|
||||
connect(patchDefault, SIGNAL(released()), this, SLOT(defaultPatchPath()));
|
||||
connect(cheatSelect, SIGNAL(released()), this, SLOT(selectCheatPath()));
|
||||
connect(cheatDefault, SIGNAL(released()), this, SLOT(defaultCheatPath()));
|
||||
connect(dataSelect, SIGNAL(released()), this, SLOT(selectDataPath()));
|
||||
connect(dataDefault, SIGNAL(released()), this, SLOT(defaultDataPath()));
|
||||
}
|
||||
|
||||
void PathSettingsWindow::syncUi() {
|
||||
gamePath->setText (snes.config.path.rom == "" ? "<startup path>" : (const char*)snes.config.path.rom);
|
||||
savePath->setText (snes.config.path.save == "" ? "<same folder as loaded game>" : (const char*)snes.config.path.save);
|
||||
patchPath->setText(snes.config.path.patch == "" ? "<same folder as loaded game>" : (const char*)snes.config.path.patch);
|
||||
cheatPath->setText(snes.config.path.cheat == "" ? "<same folder as loaded game>" : (const char*)snes.config.path.cheat);
|
||||
dataPath->setText (snes.config.path.data == "" ? "<same folder as loaded game>" : (const char*)snes.config.path.data);
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectGamePath() {
|
||||
string path = utility.selectFolder("Default Game Path");
|
||||
if(path.length() > 0) {
|
||||
snes.config.path.rom = path;
|
||||
syncUi();
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::defaultGamePath() {
|
||||
snes.config.path.rom = "";
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectSavePath() {
|
||||
string path = utility.selectFolder("Default Save RAM Path");
|
||||
if(path.length() > 0) {
|
||||
snes.config.path.save = path;
|
||||
syncUi();
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::defaultSavePath() {
|
||||
snes.config.path.save = "";
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectPatchPath() {
|
||||
string path = utility.selectFolder("Default UPS Patch Path");
|
||||
if(path.length() > 0) {
|
||||
snes.config.path.patch = path;
|
||||
syncUi();
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::defaultPatchPath() {
|
||||
snes.config.path.patch = "";
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectCheatPath() {
|
||||
string path = utility.selectFolder("Default Cheat File Path");
|
||||
if(path.length() > 0) {
|
||||
snes.config.path.cheat = path;
|
||||
syncUi();
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::defaultCheatPath() {
|
||||
snes.config.path.cheat = "";
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void PathSettingsWindow::selectDataPath() {
|
||||
string path = utility.selectFolder("Default Export Data Path");
|
||||
if(path.length() > 0) {
|
||||
snes.config.path.data = path;
|
||||
syncUi();
|
||||
}
|
||||
}
|
||||
|
||||
void PathSettingsWindow::defaultDataPath() {
|
||||
snes.config.path.data = "";
|
||||
syncUi();
|
||||
}
|
||||
49
bsnes/ui_qt/settings/paths.hpp
Executable file
49
bsnes/ui_qt/settings/paths.hpp
Executable file
@@ -0,0 +1,49 @@
|
||||
class PathSettingsWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *panel;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *title;
|
||||
QLabel *gameLabel;
|
||||
QHBoxLayout *games;
|
||||
QLineEdit *gamePath;
|
||||
QPushButton *gameSelect;
|
||||
QPushButton *gameDefault;
|
||||
QLabel *saveLabel;
|
||||
QHBoxLayout *saves;
|
||||
QLineEdit *savePath;
|
||||
QPushButton *saveSelect;
|
||||
QPushButton *saveDefault;
|
||||
QLabel *patchLabel;
|
||||
QHBoxLayout *patches;
|
||||
QLineEdit *patchPath;
|
||||
QPushButton *patchSelect;
|
||||
QPushButton *patchDefault;
|
||||
QLabel *cheatLabel;
|
||||
QHBoxLayout *cheats;
|
||||
QLineEdit *cheatPath;
|
||||
QPushButton *cheatSelect;
|
||||
QPushButton *cheatDefault;
|
||||
QLabel *dataLabel;
|
||||
QHBoxLayout *data;
|
||||
QLineEdit *dataPath;
|
||||
QPushButton *dataSelect;
|
||||
QPushButton *dataDefault;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
|
||||
public slots:
|
||||
void selectGamePath();
|
||||
void defaultGamePath();
|
||||
void selectSavePath();
|
||||
void defaultSavePath();
|
||||
void selectPatchPath();
|
||||
void defaultPatchPath();
|
||||
void selectCheatPath();
|
||||
void defaultCheatPath();
|
||||
void selectDataPath();
|
||||
void defaultDataPath();
|
||||
} *winPathSettings;
|
||||
84
bsnes/ui_qt/settings/settings.cpp
Executable file
84
bsnes/ui_qt/settings/settings.cpp
Executable file
@@ -0,0 +1,84 @@
|
||||
#include "video.cpp"
|
||||
#include "audio.cpp"
|
||||
#include "input.cpp"
|
||||
#include "paths.cpp"
|
||||
#include "cheateditor.cpp"
|
||||
#include "advanced.cpp"
|
||||
|
||||
#include "utility/inputcapture.cpp"
|
||||
#include "utility/codeeditor.cpp"
|
||||
|
||||
void SettingsWindow::setup() {
|
||||
window = new QWidget;
|
||||
window->setObjectName("settings-window");
|
||||
window->setWindowTitle("Configuration Settings");
|
||||
|
||||
list = new QListWidget;
|
||||
video = new QListWidgetItem("Video");
|
||||
audio = new QListWidgetItem("Audio");
|
||||
input = new QListWidgetItem("Input");
|
||||
paths = new QListWidgetItem("Paths");
|
||||
cheatcodes = new QListWidgetItem("Cheat Codes");
|
||||
advanced = new QListWidgetItem("Advanced");
|
||||
list->addItem(video);
|
||||
list->addItem(audio);
|
||||
list->addItem(input);
|
||||
list->addItem(paths);
|
||||
list->addItem(cheatcodes);
|
||||
list->addItem(advanced);
|
||||
list->setCurrentItem(input); //select most frequently used panel by default
|
||||
list->setFixedWidth(135);
|
||||
list->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
|
||||
panel = new QWidget;
|
||||
panel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
|
||||
layout = new QHBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(Style::WidgetSpacing);
|
||||
layout->addWidget(list);
|
||||
layout->addWidget(panel);
|
||||
window->setLayout(layout);
|
||||
|
||||
winVideoSettings = new VideoSettingsWindow;
|
||||
winAudioSettings = new AudioSettingsWindow;
|
||||
winInputSettings = new InputSettingsWindow;
|
||||
winPathSettings = new PathSettingsWindow;
|
||||
winCheatEditor = new CheatEditorWindow;
|
||||
winAdvancedSettings = new AdvancedSettingsWindow;
|
||||
winInputCapture = new InputCaptureWindow;
|
||||
winCodeEditor = new CodeEditorWindow;
|
||||
|
||||
winVideoSettings->setup();
|
||||
winAudioSettings->setup();
|
||||
winInputSettings->setup();
|
||||
winPathSettings->setup();
|
||||
winCheatEditor->setup();
|
||||
winAdvancedSettings->setup();
|
||||
winInputCapture->setup();
|
||||
winCodeEditor->setup();
|
||||
|
||||
panelLayout = new QStackedLayout(panel);
|
||||
panelLayout->addWidget(winVideoSettings->panel);
|
||||
panelLayout->addWidget(winAudioSettings->panel);
|
||||
panelLayout->addWidget(winInputSettings->panel);
|
||||
panelLayout->addWidget(winPathSettings->panel);
|
||||
panelLayout->addWidget(winCheatEditor->panel);
|
||||
panelLayout->addWidget(winAdvancedSettings->panel);
|
||||
panel->setLayout(panelLayout);
|
||||
|
||||
connect(list, SIGNAL(currentRowChanged(int)), this, SLOT(listChanged()));
|
||||
|
||||
listChanged();
|
||||
window->setMinimumSize(600, 360);
|
||||
}
|
||||
|
||||
void SettingsWindow::listChanged() {
|
||||
QListWidgetItem *item = list->currentItem();
|
||||
if(item == video) panelLayout->setCurrentWidget(winVideoSettings->panel);
|
||||
if(item == audio) panelLayout->setCurrentWidget(winAudioSettings->panel);
|
||||
if(item == input) panelLayout->setCurrentWidget(winInputSettings->panel);
|
||||
if(item == paths) panelLayout->setCurrentWidget(winPathSettings->panel);
|
||||
if(item == cheatcodes) panelLayout->setCurrentWidget(winCheatEditor->panel);
|
||||
if(item == advanced) panelLayout->setCurrentWidget(winAdvancedSettings->panel);
|
||||
}
|
||||
31
bsnes/ui_qt/settings/settings.hpp
Executable file
31
bsnes/ui_qt/settings/settings.hpp
Executable file
@@ -0,0 +1,31 @@
|
||||
#include "video.moc"
|
||||
#include "audio.moc"
|
||||
#include "input.moc"
|
||||
#include "paths.moc"
|
||||
#include "cheateditor.moc"
|
||||
#include "advanced.moc"
|
||||
|
||||
#include "utility/inputcapture.moc"
|
||||
#include "utility/codeeditor.moc"
|
||||
|
||||
class SettingsWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *window;
|
||||
QHBoxLayout *layout;
|
||||
QListWidget *list;
|
||||
QListWidgetItem *video;
|
||||
QListWidgetItem *audio;
|
||||
QListWidgetItem *input;
|
||||
QListWidgetItem *paths;
|
||||
QListWidgetItem *cheatcodes;
|
||||
QListWidgetItem *advanced;
|
||||
QWidget *panel;
|
||||
QStackedLayout *panelLayout;
|
||||
|
||||
void setup();
|
||||
|
||||
public slots:
|
||||
void listChanged();
|
||||
} *winSettings;
|
||||
180
bsnes/ui_qt/settings/utility/codeeditor.cpp
Executable file
180
bsnes/ui_qt/settings/utility/codeeditor.cpp
Executable file
@@ -0,0 +1,180 @@
|
||||
void CodeEditorWindow::setup() {
|
||||
window = new QWidget;
|
||||
window->setObjectName("code-editor-window");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(0);
|
||||
|
||||
descLabel = new QLabel("Description:");
|
||||
layout->addWidget(descLabel);
|
||||
|
||||
description = new QTextEdit;
|
||||
layout->addWidget(description);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
codeLabel = new QLabel("Cheat code(s):");
|
||||
layout->addWidget(codeLabel);
|
||||
|
||||
codeLayout = new QHBoxLayout; {
|
||||
codeLayout->setMargin(0);
|
||||
|
||||
codeList = new QListWidget;
|
||||
codeLayout->addWidget(codeList);
|
||||
codeLayout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
controls = new QVBoxLayout; {
|
||||
controls->setMargin(0);
|
||||
|
||||
codeValue = new QLineEdit;
|
||||
controls->addWidget(codeValue);
|
||||
|
||||
codeAdd = new QPushButton("Add Code");
|
||||
controls->addWidget(codeAdd);
|
||||
|
||||
codeDelete = new QPushButton("Delete Code");
|
||||
controls->addWidget(codeDelete);
|
||||
|
||||
codeDeleteAll = new QPushButton("Delete All");
|
||||
controls->addWidget(codeDeleteAll);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Expanding);
|
||||
controls->addWidget(spacer);
|
||||
}
|
||||
codeLayout->addLayout(controls);
|
||||
}
|
||||
layout->addLayout(codeLayout);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
enabled = new QCheckBox("Enable this cheat code");
|
||||
layout->addWidget(enabled);
|
||||
|
||||
finishControls = new QHBoxLayout; {
|
||||
okButton = new QPushButton("Ok");
|
||||
finishControls->addWidget(okButton);
|
||||
|
||||
cancelButton = new QPushButton("Cancel");
|
||||
finishControls->addWidget(cancelButton);
|
||||
}
|
||||
finishControls->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(finishControls);
|
||||
|
||||
window->setLayout(layout);
|
||||
window->setMinimumSize(400, 375);
|
||||
|
||||
connect(codeList, SIGNAL(itemSelectionChanged()), this, SLOT(listChanged()));
|
||||
connect(codeValue, SIGNAL(textChanged(const QString&)), this, SLOT(codeChanged()));
|
||||
connect(codeAdd, SIGNAL(released()), this, SLOT(addCodeToList()));
|
||||
connect(codeDelete, SIGNAL(released()), this, SLOT(deleteCodeFromList()));
|
||||
connect(codeDeleteAll, SIGNAL(released()), this, SLOT(deleteAllCodesFromList()));
|
||||
connect(okButton, SIGNAL(released()), this, SLOT(accept()));
|
||||
connect(cancelButton, SIGNAL(released()), this, SLOT(dismiss()));
|
||||
}
|
||||
|
||||
void CodeEditorWindow::syncUi() {
|
||||
//only activate add button when code is valid
|
||||
string code = codeValue->text().toUtf8().data();
|
||||
Cheat::cheat_t temp;
|
||||
bool valid = cheat.decode(code, temp);
|
||||
codeAdd->setEnabled(valid);
|
||||
|
||||
//only activate delete button when a code is selected
|
||||
QListWidgetItem *item = codeList->currentItem();
|
||||
codeDelete->setEnabled(item && item->isSelected());
|
||||
|
||||
//only activate delete all / ok buttons when there are one or more codes entered
|
||||
codeDeleteAll->setEnabled(codeList->count() > 0);
|
||||
okButton->setEnabled(codeList->count() > 0);
|
||||
}
|
||||
|
||||
void CodeEditorWindow::listChanged() { syncUi(); }
|
||||
void CodeEditorWindow::codeChanged() { syncUi(); }
|
||||
|
||||
void CodeEditorWindow::addCodeToList() {
|
||||
string code = codeValue->text().toUtf8().data();
|
||||
Cheat::cheat_t temp;
|
||||
if(cheat.decode(code, temp) == true) codeList->addItem(utf8() << code);
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void CodeEditorWindow::deleteCodeFromList() {
|
||||
int index = codeList->currentRow();
|
||||
if(index >= 0) {
|
||||
QListWidgetItem *item = codeList->takeItem(index);
|
||||
delete item;
|
||||
}
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void CodeEditorWindow::deleteAllCodesFromList() {
|
||||
codeList->clear();
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void CodeEditorWindow::accept() {
|
||||
string desc = description->toPlainText().toUtf8().data();
|
||||
string code;
|
||||
for(unsigned i = 0; i < codeList->count(); i++) {
|
||||
code << (codeList->item(i)->text().toUtf8().data());
|
||||
if(i != codeList->count() - 1) code << "+";
|
||||
}
|
||||
|
||||
if(activeCode == -1) {
|
||||
//adding a new code
|
||||
cheat.add(enabled->isChecked(), code, desc);
|
||||
winCheatEditor->reloadList();
|
||||
} else if(codeList->count() > 0) {
|
||||
//editing an existing code
|
||||
cheat.edit(activeCode, enabled->isChecked(), code, desc);
|
||||
winCheatEditor->updateList();
|
||||
} else {
|
||||
//deleting an existing code
|
||||
cheat.remove(activeCode);
|
||||
winCheatEditor->reloadList();
|
||||
}
|
||||
|
||||
dismiss();
|
||||
}
|
||||
|
||||
void CodeEditorWindow::dismiss() {
|
||||
activeCode = -1;
|
||||
window->hide();
|
||||
}
|
||||
|
||||
void CodeEditorWindow::addCode() {
|
||||
activeCode = -1;
|
||||
description->setPlainText("");
|
||||
codeList->clear();
|
||||
codeValue->setText("");
|
||||
enabled->setCheckState(Qt::Unchecked);
|
||||
showWindow("Add New Cheat Code");
|
||||
}
|
||||
|
||||
void CodeEditorWindow::editCode(unsigned code) {
|
||||
activeCode = code;
|
||||
codeList->clear();
|
||||
codeValue->setText("");
|
||||
|
||||
Cheat::cheat_t item;
|
||||
cheat.get(activeCode, item);
|
||||
|
||||
description->setPlainText(utf8() << item.desc);
|
||||
|
||||
lstring part;
|
||||
part.split("+", item.code);
|
||||
|
||||
for(unsigned i = 0; i < item.count; i++) codeList->addItem(utf8() << part[i]);
|
||||
enabled->setCheckState(item.enabled ? Qt::Checked : Qt::Unchecked);
|
||||
showWindow("Edit Existing Cheat Code");
|
||||
}
|
||||
|
||||
void CodeEditorWindow::showWindow(const char *title) {
|
||||
syncUi();
|
||||
window->setWindowTitle(title);
|
||||
utility.showCentered(window);
|
||||
}
|
||||
|
||||
CodeEditorWindow::CodeEditorWindow() {
|
||||
activeCode = -1;
|
||||
}
|
||||
43
bsnes/ui_qt/settings/utility/codeeditor.hpp
Executable file
43
bsnes/ui_qt/settings/utility/codeeditor.hpp
Executable file
@@ -0,0 +1,43 @@
|
||||
class CodeEditorWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *window;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *descLabel;
|
||||
QTextEdit *description;
|
||||
QLabel *codeLabel;
|
||||
QHBoxLayout *codeLayout;
|
||||
QListWidget *codeList;
|
||||
QVBoxLayout *controls;
|
||||
QLineEdit *codeValue;
|
||||
QPushButton *codeAdd;
|
||||
QPushButton *codeDelete;
|
||||
QPushButton *codeDeleteAll;
|
||||
QWidget *spacer;
|
||||
QCheckBox *enabled;
|
||||
QHBoxLayout *finishControls;
|
||||
QPushButton *okButton;
|
||||
QPushButton *cancelButton;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
void addCode();
|
||||
void editCode(unsigned code);
|
||||
CodeEditorWindow();
|
||||
|
||||
public slots:
|
||||
void listChanged();
|
||||
void codeChanged();
|
||||
void addCodeToList();
|
||||
void deleteCodeFromList();
|
||||
void deleteAllCodesFromList();
|
||||
void accept();
|
||||
void dismiss();
|
||||
|
||||
private:
|
||||
signed activeCode;
|
||||
void showWindow(const char *title);
|
||||
|
||||
friend class CheatEditorWindow;
|
||||
} *winCodeEditor;
|
||||
454
bsnes/ui_qt/settings/utility/inputcapture.cpp
Executable file
454
bsnes/ui_qt/settings/utility/inputcapture.cpp
Executable file
@@ -0,0 +1,454 @@
|
||||
void InputCaptureWindow::setup() {
|
||||
window = new Window;
|
||||
window->setObjectName("input-capture-window");
|
||||
window->setWindowTitle("Input Capture");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(0);
|
||||
|
||||
hlayout = new QHBoxLayout;
|
||||
hlayout->setSpacing(Style::WidgetSpacing); {
|
||||
title = new QLabel;
|
||||
hlayout->addWidget(title, 0, Qt::AlignTop);
|
||||
|
||||
mouseAxes = new QPushButton(" Assign Mouse Axis ");
|
||||
mouseAxes->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
hlayout->addWidget(mouseAxes, 0, Qt::AlignTop);
|
||||
|
||||
mouseButtons = new QPushButton(" Assign Mouse Button ");
|
||||
mouseButtons->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
|
||||
hlayout->addWidget(mouseButtons, 0, Qt::AlignTop);
|
||||
}
|
||||
layout->addLayout(hlayout);
|
||||
|
||||
imageSpacer = new QWidget;
|
||||
imageSpacer->setFixedSize(Style::WidgetSpacing, Style::WidgetSpacing);
|
||||
layout->addWidget(imageSpacer);
|
||||
|
||||
imageWidget = new ImageWidget;
|
||||
layout->addWidget(imageWidget, 0, Qt::AlignHCenter);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
window->setLayout(layout);
|
||||
connect(mouseAxes, SIGNAL(released()), this, SLOT(assignMouseAxis()));
|
||||
connect(mouseButtons, SIGNAL(released()), this, SLOT(assignMouseButton()));
|
||||
|
||||
winInputMouseCaptureWindow = new InputMouseCaptureWindow;
|
||||
winInputMouseCaptureWindow->setup();
|
||||
|
||||
winInputCalibration = new InputCalibrationWindow;
|
||||
winInputCalibration->setup();
|
||||
}
|
||||
|
||||
void InputCaptureWindow::activate(InputObject *object) {
|
||||
if(!activeGroup) window->hide();
|
||||
|
||||
utf8 info;
|
||||
info << "<b>ID:</b> ";
|
||||
if(object->parent) {
|
||||
InputDevice *device = dynamic_cast<InputDevice*>(object->parent);
|
||||
if(device) info << "Controller port " << (int)(device->port + 1) << " <b>::</b> ";
|
||||
else info << "User interface <b>::</b> ";
|
||||
info << object->parent->name << " <b>::</b> ";
|
||||
}
|
||||
info << object->name << "<br>";
|
||||
|
||||
activeObject = object;
|
||||
if(activeObject->type == InputObject::Button) {
|
||||
mouseAxes->hide();
|
||||
mouseButtons->show();
|
||||
|
||||
info << "Press any key or button to assign to this ID.";
|
||||
} else /*(activeObject->type == InputObject::Axis)*/ {
|
||||
mouseAxes->show();
|
||||
mouseButtons->hide();
|
||||
|
||||
info << "Move any axis to assign to this ID.";
|
||||
}
|
||||
|
||||
if(dynamic_cast<Joypad*>(activeObject->parent)) {
|
||||
imageSpacer->show();
|
||||
imageWidget->setFixedSize(480, 210);
|
||||
imageWidget->show();
|
||||
} else {
|
||||
imageSpacer->hide();
|
||||
imageWidget->hide();
|
||||
}
|
||||
|
||||
title->setText(info);
|
||||
utility.showCentered(window);
|
||||
}
|
||||
|
||||
void InputCaptureWindow::activate(InputGroup *group) {
|
||||
activeGroup = group;
|
||||
groupIndex = 0;
|
||||
window->hide();
|
||||
activate((*activeGroup)[groupIndex]);
|
||||
}
|
||||
|
||||
void InputCaptureWindow::inputEvent(uint16_t code, bool forceAssign /* = false */) {
|
||||
if(!activeObject || inputLock == true) return;
|
||||
|
||||
//input polling is global, need to block mouse actions that may be UI interactions.
|
||||
//custom controls on window allow mouse assignment instead.
|
||||
if(forceAssign == false) {
|
||||
if(winInputMouseCaptureWindow->window->isActiveWindow()) {
|
||||
winInputMouseCaptureWindow->inputEvent(code);
|
||||
return;
|
||||
}
|
||||
if(!window->isActiveWindow()) return;
|
||||
|
||||
//get as much info as possible about this code
|
||||
InputCode::type_t type = InputCode::type(code);
|
||||
InputCode::axistype_t axisType = InputCode::axisType(code);
|
||||
int joypadNumber = InputCode::joypadNumber(code);
|
||||
int16_t state = inputManager.state(code);
|
||||
unsigned distance = abs(state - inputManager.lastState(code));
|
||||
|
||||
if(type == InputCode::JoypadHat) {
|
||||
//hats can be in any of nine clock-wise states (4x direct, 4x angled and centered.)
|
||||
//only map when hat is moved to an explicit direction.
|
||||
if(state != joypad<>::hat_up && state != joypad<>::hat_down
|
||||
&& state != joypad<>::hat_left && state != joypad<>::hat_right) return;
|
||||
}
|
||||
|
||||
if(type == InputCode::JoypadAxis) {
|
||||
//require a degree of resistance to prevent accidental mapping
|
||||
if(axisType == InputCode::Stick) {
|
||||
//require axis to be pressed almost completely toward a specific direction
|
||||
if(state > -28672 && state < +28672) return;
|
||||
} else if(axisType == InputCode::Trigger) {
|
||||
//require trigger to be at least 75% pressed down
|
||||
if(state > -16384) return;
|
||||
} else {
|
||||
//invalid axis type: most likely the controller has yet to be calibrated
|
||||
if(joypadNumber < 0) return; //should never occur
|
||||
|
||||
//some analog triggers report phantom motion even when user does not touch gamepad
|
||||
if(distance < 64) return; //require some degree of force to trigger calibration
|
||||
if(state > -8192 && state < +8192) return; //ignore center motion
|
||||
|
||||
if(inputManager.calibrated(joypadNumber) == false) {
|
||||
winInputCalibration->activate(joypadNumber);
|
||||
}
|
||||
|
||||
//block assignment until controller is fully calibrated
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(activeObject->type == InputObject::Axis) {
|
||||
if(type == InputCode::KeyboardButton
|
||||
|| type == InputCode::MouseAxis
|
||||
|| type == InputCode::MouseButton
|
||||
|| type == InputCode::JoypadHat
|
||||
|| type == InputCode::JoypadButton
|
||||
) return;
|
||||
|
||||
//uni-directional trigger cannot map to bi-directional axis
|
||||
if(type == InputCode::JoypadAxis && axisType == InputCode::Trigger) return;
|
||||
}
|
||||
|
||||
if(activeObject->type == InputObject::Button) {
|
||||
if(type == InputCode::MouseAxis
|
||||
|| type == InputCode::MouseButton
|
||||
) return;
|
||||
|
||||
//only capture on button press, not release
|
||||
if(type != InputCode::JoypadAxis && state == false) return;
|
||||
}
|
||||
|
||||
//if assigning a complete controller set, ensure requested key has not been assigned
|
||||
//to a previous entry in the group already. this prevents slow motion of joypad axes
|
||||
//from assigning the same value to multiple entries in rapid succession.
|
||||
if(activeGroup && groupIndex > 0) {
|
||||
for(unsigned i = 0; i < groupIndex; i++) {
|
||||
if(code == (*activeGroup)[i]->code) {
|
||||
//joypad hats and axes have multiple states, and are differentiated by modifier.
|
||||
//allow mapping only if requested modifier is unique.
|
||||
if(type == InputCode::JoypadHat) {
|
||||
if(state == joypad<>::hat_up && (*activeGroup)[i]->modifier == InputObject::Up ) return;
|
||||
if(state == joypad<>::hat_down && (*activeGroup)[i]->modifier == InputObject::Down ) return;
|
||||
if(state == joypad<>::hat_left && (*activeGroup)[i]->modifier == InputObject::Left ) return;
|
||||
if(state == joypad<>::hat_right && (*activeGroup)[i]->modifier == InputObject::Right) return;
|
||||
} else if(type == InputCode::JoypadAxis) {
|
||||
if(axisType == InputCode::Stick) {
|
||||
if(state < 0 && (*activeGroup)[i]->modifier == InputObject::Lo) return;
|
||||
if(state >= 0 && (*activeGroup)[i]->modifier == InputObject::Hi) return;
|
||||
} else if(axisType == InputCode::Trigger) {
|
||||
if((*activeGroup)[i]->modifier == InputObject::Trigger) return;
|
||||
}
|
||||
} else {
|
||||
//this code has already been used, do not map it
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//bind code and update GUI input assignment list
|
||||
activeObject->bind(code);
|
||||
winInputSettings->updateList();
|
||||
activeObject = 0;
|
||||
|
||||
//ignore multiple simultaneous state changes.
|
||||
//this helps with joypads that only activate
|
||||
//analog inputs after the first button press.
|
||||
inputLock = true;
|
||||
for(unsigned i = 0; i < 2; i++) inputManager.refresh();
|
||||
inputLock = false;
|
||||
|
||||
if(!activeGroup) {
|
||||
window->hide();
|
||||
winInputMouseCaptureWindow->window->hide();
|
||||
} else {
|
||||
//try and map the next code in this input group
|
||||
groupIndex++;
|
||||
if(groupIndex < activeGroup->size()) {
|
||||
activate((*activeGroup)[groupIndex]);
|
||||
} else {
|
||||
//all group codes mapped
|
||||
window->hide();
|
||||
winInputMouseCaptureWindow->window->hide();
|
||||
activeGroup = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputCaptureWindow::assignMouseAxis() {
|
||||
//refresh input state so that mouse release event (from SIGNAL(released())
|
||||
//is not sent immediately after window is visible.
|
||||
inputManager.refresh();
|
||||
winInputMouseCaptureWindow->activate(InputMouseCaptureWindow::AxisMode);
|
||||
}
|
||||
|
||||
void InputCaptureWindow::assignMouseButton() {
|
||||
inputManager.refresh();
|
||||
winInputMouseCaptureWindow->activate(InputMouseCaptureWindow::ButtonMode);
|
||||
}
|
||||
|
||||
InputCaptureWindow::InputCaptureWindow() {
|
||||
activeObject = 0;
|
||||
activeGroup = 0;
|
||||
groupIndex = 0;
|
||||
inputLock = false;
|
||||
}
|
||||
|
||||
void InputCaptureWindow::Window::closeEvent(QCloseEvent*) {
|
||||
//window closed by user, cancel key assignment
|
||||
winInputCapture->activeObject = 0;
|
||||
winInputCapture->activeGroup = 0;
|
||||
}
|
||||
|
||||
void InputCaptureWindow::ImageWidget::paintEvent(QPaintEvent*) {
|
||||
//currently, there is only an image available for the joypad.
|
||||
//in the future, this routine should determine which type of
|
||||
//image to draw via activeObject->parent's derived class type.
|
||||
QPainter painter(this);
|
||||
QPixmap pixmap(":/joypad.png");
|
||||
painter.drawPixmap(0, 0, pixmap);
|
||||
}
|
||||
|
||||
//=======================
|
||||
//InputMouseCaptureWindow
|
||||
//=======================
|
||||
|
||||
void InputMouseCaptureWindow::setup() {
|
||||
window = new QWidget;
|
||||
window->setObjectName("input-mouse-capture-window");
|
||||
window->setWindowTitle("Mouse Input Capture");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(0);
|
||||
|
||||
info = new QLabel;
|
||||
layout->addWidget(info);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
captureBox = new QLabel("[ capture box ]");
|
||||
captureBox->setObjectName("mouse-capture-box");
|
||||
captureBox->setAlignment(Qt::AlignCenter);
|
||||
captureBox->setFixedHeight(120);
|
||||
layout->addWidget(captureBox);
|
||||
|
||||
buttonLayout = new QHBoxLayout;
|
||||
buttonLayout->setSpacing(Style::WidgetSpacing); {
|
||||
xAxis = new QPushButton("X-axis");
|
||||
buttonLayout->addWidget(xAxis);
|
||||
|
||||
yAxis = new QPushButton("Y-axis");
|
||||
buttonLayout->addWidget(yAxis);
|
||||
}
|
||||
layout->addLayout(buttonLayout);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
window->setLayout(layout);
|
||||
connect(xAxis, SIGNAL(released()), this, SLOT(assignAxisX()));
|
||||
connect(yAxis, SIGNAL(released()), this, SLOT(assignAxisY()));
|
||||
}
|
||||
|
||||
void InputMouseCaptureWindow::activate(InputMouseCaptureWindow::Mode mode_) {
|
||||
window->hide();
|
||||
activeMode = mode_;
|
||||
|
||||
if(activeMode == AxisMode) {
|
||||
captureBox->hide();
|
||||
xAxis->show();
|
||||
yAxis->show();
|
||||
|
||||
info->setText(utf8()
|
||||
<< "To assign a mouse axis to this ID, please click the desired axis button below,<br>"
|
||||
<< "using the mouse that you want the axis to be assigned to."
|
||||
);
|
||||
|
||||
activeMouse = -1;
|
||||
} else /*(activeMode == ButtonMode) */ {
|
||||
captureBox->show();
|
||||
xAxis->hide();
|
||||
yAxis->hide();
|
||||
|
||||
info->setText(utf8()
|
||||
<< "To assign a mouse button to this ID, please place the mouse you wish to map<br>"
|
||||
<< "over the capture box below, and then click any mouse button to set assignment."
|
||||
);
|
||||
}
|
||||
|
||||
utility.showCentered(window);
|
||||
}
|
||||
|
||||
//this is only called when isActiveWindow() == true
|
||||
void InputMouseCaptureWindow::inputEvent(uint16_t code) {
|
||||
InputCode::type_t type = InputCode::type(code);
|
||||
int16_t state = inputManager.state(code);
|
||||
|
||||
if(activeMode == AxisMode) {
|
||||
//when pressing down mouse button (eg to select "X-axis" or "Y-axis"),
|
||||
//record mouse index for later assignment
|
||||
if(type == InputCode::MouseButton && state == true) {
|
||||
activeMouse = InputCode::mouseNumber(code);
|
||||
return;
|
||||
}
|
||||
} else if(activeMode == ButtonMode) {
|
||||
//if this is a mouse button that is being released ...
|
||||
if(type == InputCode::MouseButton && state == false) {
|
||||
//ensure button was clicked inside active capture box
|
||||
QRect windowRect = window->geometry();
|
||||
QRect widgetRect = captureBox->geometry();
|
||||
unsigned wx = windowRect.left() + widgetRect.left();
|
||||
unsigned wy = windowRect.top() + widgetRect.top();
|
||||
unsigned px = QCursor::pos().x();
|
||||
unsigned py = QCursor::pos().y();
|
||||
|
||||
if(px < wx || px >= wx + widgetRect.size().width() ) return;
|
||||
if(py < wy || py >= wy + widgetRect.size().height()) return;
|
||||
|
||||
winInputCapture->inputEvent(code, true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InputMouseCaptureWindow::assignAxisX() {
|
||||
if(activeMouse >= 0) {
|
||||
winInputCapture->inputEvent(mouse<>::index(activeMouse, mouse<>::x), true);
|
||||
}
|
||||
}
|
||||
|
||||
void InputMouseCaptureWindow::assignAxisY() {
|
||||
if(activeMouse >= 0) {
|
||||
winInputCapture->inputEvent(mouse<>::index(activeMouse, mouse<>::y), true);
|
||||
}
|
||||
}
|
||||
|
||||
//====================
|
||||
//InputCalibrateWindow
|
||||
//====================
|
||||
|
||||
//background:
|
||||
//===========
|
||||
//HID devices work by sending device state *changes* only. what this means is that when an application is started,
|
||||
//it does not know the current state of said device. the keyboard and mouse are exceptions, as the OS globally
|
||||
//tracks their states. but this does apply to joypads. once a button is pressed, or an axis is moved, the entire
|
||||
//joypad state will be sent in a message, that APIs such as DirectInput and SDL will buffer.
|
||||
//
|
||||
//to complicate matters, recent joypads have added pressure-sensitive buttons (triggers), in addition to the
|
||||
//existing analog sticks. but this functionality was not extended to the USB HID state or to various platform
|
||||
//input APIs. instead, they are treated exactly like axes.
|
||||
//
|
||||
//however, an application needs to treat these two input types distinctly:
|
||||
//a stick is a bi-directional input. the stick starts off centered, and can be moved left or right, or up or down.
|
||||
//a trigger is a uni-directional input. it can only be pushed down.
|
||||
//
|
||||
//a stick's default, unpressed state is centered (0), whereas a trigger's default state is fully depressed (+32767.)
|
||||
//but because the default state is not available until the user presses a button on a joypad, it is not possible to
|
||||
//calibrate a joypad on startup. all axes will report a value of 0, even if buttons are depressed.
|
||||
//
|
||||
//thusly, this class is needed. it will spawn a window upon the first attempt to map a joypad axis after startup.
|
||||
//by the point this window appears, an axis must have been moved, so the joypad state is now valid. but since it's
|
||||
//not possible to tell which button was pressed or which axis was moved, it's possible the axis that we're trying to
|
||||
//map was moved. so querying its state now might result in improper mapping. so instead, this window is shown, and
|
||||
//the user is asked not to press any buttons or move any axes. after hitting okay to confirm the joypad is idle,
|
||||
//the joypad can finally be calibrated properly.
|
||||
//
|
||||
//upon assignment, the calibration data is appended to the input assignment value (eg "joypad00.axis00::trigger"),
|
||||
//so that calibration is not necessary every time the emulator is run -- only when modifying input mapping on an axis.
|
||||
|
||||
void InputCalibrationWindow::activate(unsigned joy) {
|
||||
//do not override an already active calibration window
|
||||
if(window->isVisible()) return;
|
||||
|
||||
activeJoypad = joy;
|
||||
info->setText(utf8()
|
||||
<< "Joypad #" << joy << " needs to be calibrated before it can be mapped. "
|
||||
<< "Please ensure that<br>no buttons are pressed, "
|
||||
<< "and all axes are centered before pressing okay."
|
||||
);
|
||||
|
||||
utility.showCentered(window);
|
||||
ok->setFocus();
|
||||
}
|
||||
|
||||
void InputCalibrationWindow::setup() {
|
||||
window = new Window;
|
||||
window->setObjectName("input-calibrate-window");
|
||||
window->setWindowTitle("Joypad Calibration");
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(Style::WindowMargin);
|
||||
layout->setSpacing(0);
|
||||
|
||||
info = new QLabel;
|
||||
layout->addWidget(info);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
ok = new QPushButton("Ok");
|
||||
layout->addWidget(ok);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
window->setLayout(layout);
|
||||
connect(ok, SIGNAL(released()), this, SLOT(dismiss()));
|
||||
}
|
||||
|
||||
void InputCalibrationWindow::dismiss() {
|
||||
window->hide();
|
||||
if(activeJoypad != -1) {
|
||||
inputManager.calibrate(activeJoypad);
|
||||
activeJoypad = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void InputCalibrationWindow::Window::closeEvent(QCloseEvent*) {
|
||||
winInputCalibration->dismiss();
|
||||
}
|
||||
88
bsnes/ui_qt/settings/utility/inputcapture.hpp
Executable file
88
bsnes/ui_qt/settings/utility/inputcapture.hpp
Executable file
@@ -0,0 +1,88 @@
|
||||
class InputCaptureWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Window : public QWidget {
|
||||
void closeEvent(QCloseEvent*);
|
||||
} *window;
|
||||
QVBoxLayout *layout;
|
||||
QHBoxLayout *hlayout;
|
||||
QLabel *title;
|
||||
QPushButton *mouseAxes;
|
||||
QPushButton *mouseButtons;
|
||||
QWidget *imageSpacer;
|
||||
struct ImageWidget : public QWidget {
|
||||
void paintEvent(QPaintEvent*);
|
||||
} *imageWidget;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void activate(InputObject *object);
|
||||
void activate(InputGroup *group);
|
||||
void inputEvent(uint16_t code, bool forceAssign = false);
|
||||
InputCaptureWindow();
|
||||
|
||||
public slots:
|
||||
void assignMouseAxis();
|
||||
void assignMouseButton();
|
||||
|
||||
private:
|
||||
InputObject *activeObject;
|
||||
InputGroup *activeGroup;
|
||||
unsigned groupIndex;
|
||||
bool inputLock;
|
||||
|
||||
friend class InputCaptureWindow::Window;
|
||||
} *winInputCapture;
|
||||
|
||||
class InputMouseCaptureWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum Mode { AxisMode, ButtonMode };
|
||||
|
||||
QWidget *window;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *info;
|
||||
QLabel *captureBox;
|
||||
QHBoxLayout *buttonLayout;
|
||||
QPushButton *xAxis;
|
||||
QPushButton *yAxis;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void activate(Mode);
|
||||
void inputEvent(uint16_t code);
|
||||
|
||||
public slots:
|
||||
void assignAxisX();
|
||||
void assignAxisY();
|
||||
|
||||
private:
|
||||
Mode activeMode;
|
||||
signed activeMouse;
|
||||
} *winInputMouseCaptureWindow;
|
||||
|
||||
class InputCalibrationWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct Window : public QWidget {
|
||||
void closeEvent(QCloseEvent*);
|
||||
} *window;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *info;
|
||||
QPushButton *ok;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void activate(unsigned joy);
|
||||
|
||||
public slots:
|
||||
void dismiss();
|
||||
|
||||
private:
|
||||
int activeJoypad;
|
||||
|
||||
friend class InputCalibrationWindow::Window;
|
||||
} *winInputCalibration;
|
||||
119
bsnes/ui_qt/settings/video.cpp
Executable file
119
bsnes/ui_qt/settings/video.cpp
Executable file
@@ -0,0 +1,119 @@
|
||||
void VideoSettingsWindow::setup() {
|
||||
panel = new QWidget;
|
||||
|
||||
layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
title = new QLabel("Video Settings");
|
||||
title->setProperty("class", "title");
|
||||
layout->addWidget(title);
|
||||
|
||||
sliders = new QGridLayout; {
|
||||
lcontrast = new QLabel("Contrast adjust: +100%");
|
||||
sliders->addWidget(lcontrast, 0, 0);
|
||||
|
||||
contrast = new QSlider(Qt::Horizontal);
|
||||
contrast->setMinimum(-95);
|
||||
contrast->setMaximum(+95);
|
||||
sliders->addWidget(contrast, 0, 1);
|
||||
|
||||
lbrightness = new QLabel("Brightness adjust: +100%");
|
||||
sliders->addWidget(lbrightness, 1, 0);
|
||||
|
||||
brightness = new QSlider(Qt::Horizontal);
|
||||
brightness->setMinimum(-95);
|
||||
brightness->setMaximum(+95);
|
||||
sliders->addWidget(brightness, 1, 1);
|
||||
|
||||
lgamma = new QLabel("Gamma adjust: +100%");
|
||||
sliders->addWidget(lgamma, 2, 0);
|
||||
|
||||
gamma = new QSlider(Qt::Horizontal);
|
||||
gamma->setMinimum(-95);
|
||||
gamma->setMaximum(+95);
|
||||
sliders->addWidget(gamma, 2, 1);
|
||||
}
|
||||
sliders->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(sliders);
|
||||
layout->addSpacing(Style::WidgetSpacing);
|
||||
|
||||
options = new QHBoxLayout; {
|
||||
options->setMargin(0);
|
||||
|
||||
enableGammaRamp = new QCheckBox("Simulate NTSC TV gamma ramp");
|
||||
enableGammaRamp->setToolTip("Lower monitor gamma to more accurately match a CRT television");
|
||||
options->addWidget(enableGammaRamp);
|
||||
|
||||
enableNtscMergeFields = new QCheckBox("Merge scan fields for NTSC filter");
|
||||
enableNtscMergeFields->setToolTip(
|
||||
"NTSC filter requires 60hz w/video sync to simulate alternating field effect.\n"
|
||||
"If this is not the case, this option should be enabled to prevent excessive video shimmering."
|
||||
);
|
||||
options->addWidget(enableNtscMergeFields);
|
||||
}
|
||||
options->setSpacing(Style::WidgetSpacing);
|
||||
layout->addLayout(options);
|
||||
|
||||
spacer = new QWidget;
|
||||
spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
|
||||
layout->addWidget(spacer);
|
||||
|
||||
panel->setLayout(layout);
|
||||
connect(contrast, SIGNAL(valueChanged(int)), this, SLOT(contrastAdjust(int)));
|
||||
connect(brightness, SIGNAL(valueChanged(int)), this, SLOT(brightnessAdjust(int)));
|
||||
connect(gamma, SIGNAL(valueChanged(int)), this, SLOT(gammaAdjust(int)));
|
||||
connect(enableGammaRamp, SIGNAL(stateChanged(int)), this, SLOT(gammaRampToggle(int)));
|
||||
connect(enableNtscMergeFields, SIGNAL(stateChanged(int)), this, SLOT(ntscFieldsToggle(int)));
|
||||
|
||||
syncUi();
|
||||
}
|
||||
|
||||
void VideoSettingsWindow::syncUi() {
|
||||
int n;
|
||||
|
||||
n = config.video.contrastAdjust;
|
||||
lcontrast->setText(utf8() << "Contrast adjust: " << (n > 0 ? "+" : "") << n << "%");
|
||||
contrast->setSliderPosition(n);
|
||||
|
||||
n = config.video.brightnessAdjust;
|
||||
lbrightness->setText(utf8() << "Brightness adjust: " << (n > 0 ? "+" : "") << n << "%");
|
||||
brightness->setSliderPosition(n);
|
||||
|
||||
n = config.video.gammaAdjust;
|
||||
lgamma->setText(utf8() << "Gamma adjust: " << (n > 0 ? "+" : "") << n << "%");
|
||||
gamma->setSliderPosition(n);
|
||||
|
||||
enableGammaRamp->setChecked(config.video.enableGammaRamp);
|
||||
enableNtscMergeFields->setChecked(config.video.enableNtscMergeFields);
|
||||
}
|
||||
|
||||
void VideoSettingsWindow::gammaRampToggle(int state) {
|
||||
config.video.enableGammaRamp = (state == Qt::Checked);
|
||||
syncUi();
|
||||
utility.updateColorFilter();
|
||||
}
|
||||
|
||||
void VideoSettingsWindow::ntscFieldsToggle(int state) {
|
||||
config.video.enableNtscMergeFields = (state == Qt::Checked);
|
||||
syncUi();
|
||||
utility.updateSoftwareFilter();
|
||||
}
|
||||
|
||||
void VideoSettingsWindow::contrastAdjust(int value) {
|
||||
config.video.contrastAdjust = value;
|
||||
syncUi();
|
||||
utility.updateColorFilter();
|
||||
}
|
||||
|
||||
void VideoSettingsWindow::brightnessAdjust(int value) {
|
||||
config.video.brightnessAdjust = value;
|
||||
syncUi();
|
||||
utility.updateColorFilter();
|
||||
}
|
||||
|
||||
void VideoSettingsWindow::gammaAdjust(int value) {
|
||||
config.video.gammaAdjust = value;
|
||||
syncUi();
|
||||
utility.updateColorFilter();
|
||||
}
|
||||
29
bsnes/ui_qt/settings/video.hpp
Executable file
29
bsnes/ui_qt/settings/video.hpp
Executable file
@@ -0,0 +1,29 @@
|
||||
class VideoSettingsWindow : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QWidget *panel;
|
||||
QVBoxLayout *layout;
|
||||
QLabel *title;
|
||||
QGridLayout *sliders;
|
||||
QLabel *lcontrast;
|
||||
QSlider *contrast;
|
||||
QLabel *lbrightness;
|
||||
QSlider *brightness;
|
||||
QLabel *lgamma;
|
||||
QSlider *gamma;
|
||||
QHBoxLayout *options;
|
||||
QCheckBox *enableGammaRamp;
|
||||
QCheckBox *enableNtscMergeFields;
|
||||
QWidget *spacer;
|
||||
|
||||
void setup();
|
||||
void syncUi();
|
||||
|
||||
public slots:
|
||||
void gammaRampToggle(int);
|
||||
void ntscFieldsToggle(int);
|
||||
void contrastAdjust(int);
|
||||
void brightnessAdjust(int);
|
||||
void gammaAdjust(int);
|
||||
} *winVideoSettings;
|
||||
Reference in New Issue
Block a user