From 0e3f8d2915bcb26897403e667bb9823ef51e3b4f Mon Sep 17 00:00:00 2001 From: Jonian Guveli Date: Sun, 1 Dec 2024 16:25:59 +0200 Subject: [PATCH] Qt: Add config window controls --- CMakeLists.txt | 2 + docs/img/battery_icon.png | Bin 0 -> 4395 bytes docs/img/display_icon.png | Bin 0 -> 166 bytes docs/img/sdcard_icon.png | Bin 0 -> 4400 bytes docs/img/settings_icon.png | Bin 0 -> 697 bytes docs/img/sparkling_icon.png | Bin 0 -> 549 bytes docs/img/speaker_icon.png | Bin 0 -> 507 bytes include/panda_qt/config_window.hpp | 26 +- src/panda_qt/config_window.cpp | 376 ++++++++++++++++++++++++++++- src/panda_qt/main_window.cpp | 2 +- 10 files changed, 395 insertions(+), 11 deletions(-) create mode 100644 docs/img/battery_icon.png create mode 100644 docs/img/display_icon.png create mode 100644 docs/img/sdcard_icon.png create mode 100644 docs/img/settings_icon.png create mode 100644 docs/img/sparkling_icon.png create mode 100644 docs/img/speaker_icon.png diff --git a/CMakeLists.txt b/CMakeLists.txt index d3ca834fd..659c0d84f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -695,6 +695,8 @@ if(NOT BUILD_HYDRA_CORE AND NOT BUILD_LIBRETRO_CORE) PREFIX "/" FILES docs/img/rsob_icon.png docs/img/rstarstruck_icon.png docs/img/rpog_icon.png docs/img/rsyn_icon.png + docs/img/settings_icon.png docs/img/display_icon.png docs/img/speaker_icon.png + docs/img/sparkling_icon.png docs/img/battery_icon.png docs/img/sdcard_icon.png ) else() set(FRONTEND_SOURCE_FILES src/panda_sdl/main.cpp src/panda_sdl/frontend_sdl.cpp src/panda_sdl/mappings.cpp) diff --git a/docs/img/battery_icon.png b/docs/img/battery_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..5768a928078c9e12c03339b69d960bc84540bdb2 GIT binary patch literal 4395 zcmeHKYitzP72d@$>o}Mi%cZFUVi=Qz*X%s@J@VSv-VM7o3$DGvHX;o(vv=1M@5?*0 z-nA7f6Jj2PQXoz!!gWoQnnxnj(jd2WAa;0GB7wxIDo|ToPzq8(PE|+(v6`HjUB8-0 zk)`CHcXjukIrp6JocrB#k3HKfSFg;;F38qsG&ydUqYCCnm60_QeuonF&tSTYYTbUf zd#NT19>7xjkVdQ6z|c;RXJ|7t8KBz&BM)T7Mi~X#Om*G~@?&Ya9puGnn;9U}+8LT0 zm^)xFAm_t;6o$Ap73&np$|phwdnz$TPg8n>75pj2$kGf;(HL!JX)DW6 z5MSFho{@sIZ_eQxkYAfvUS8=gFUO*>h+hmMjV9T#agW_~x-!3QO||p=qnqbD<}uzU zcXZ*qPGma|=RViAcK)iLJkWUx_U8X!<-m!dp-cQ<2F}*i=xR!HN3@Mi(JFaQ-f%@W z!N2u!a>32NOUdEl6^Hlj_s7p@JiYIFalQZe>-=Arm1T7;IQh%n`_V;rRo&mJ)Hae%$~ciyj`X9m$;9ec=N&>>bE)T*Y_IH_x|uV z?Uv42SK6Oh;;OiCbJM4W2i?o5wa7EEx)hB{pftRa_Qi zMNQ(EY9cq-@meUP@<q}%8I(3iHU&w815wN z(Y>q2VCC_!j))Lf(sMiPxDucBMFi2usv^(x7RF|^5k8CANEii!K=5W{Bg|%#$xN9o zMhfv0pxj|e=E4F}pa5Ji0*=wl8~g?nLkL!IG%{8n!6D8-@D@QZAjI>6jhg_mCMH5v za>0pNDNsIuqKyVCZM6ynZ81PLw4WxdCf-k21;$KSOg^K*pMvrU>{F3gh=b!4LtFrn z(QrVGp%BiNR=Vvtqo>9-l|fGSgM%GkC5Ge4@dd9KLe(;-un92@l$o(m6l0={Cd0VV zS`?F@5*19fd8#YQv9PciAeK|=6aZ8`?1e3lAx@6Oypc%Ij*qq7v1JcjCm$zs4o*e@ zI@XB2qm6hO1${Y{z5?fo_(Xs6H(4u}2P*=5IC7ULLHuS_mAX;Ys4?}FdJKx{Rl+d! zQm~wm+JeM2A)lHjV5OFXdM+G5&^^-SI;t1HrW6F5&BRy@JV9|rD`7-NI6n(A6BK0- ztTr<;nJw1I=u*TlC%70Y4FHe86_ls?Ckk7v9@LV_)`@zgoB|L=&kq4t|>7vCFAMp`qt>m9)F!eVfZg70WVAcLyP|jFIqGCij@w{7s|Kmp?(3X^KM6}*LD6*`@-4ptjKj<%blm|-;3?& z&s%zUVAJW-n}!Nr>zLswTX8TechQFay?2GHuZhoHJ>GLE_(b3R62s?-gWcUrU(qkh z?0=~K<;yJ@2lwBR5|0y$oJY^qd|>U(a0j{`x!o#kpS`737kqd51;e)MS9QfJ`dUAm ze=B3*$C?)ZCl_}u<}k53YOHYo-n-EwKep?g!NyQ)!{!73uK48Q?aa%o=Ktvo{mjcd zk1RNL$We6Q)jtkyoqJ$stJrpo5Baw~(DmFWOVIjRTVLF^W;tw0<94og^ptIQ;Y%A_ B>$Cs> literal 0 HcmV?d00001 diff --git a/docs/img/display_icon.png b/docs/img/display_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cf6a68beb1ab40b9f6ba4c372e3860bd2afbf8f7 GIT binary patch literal 166 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaN3?zjj6;1;w#^NA%Cx&(BWL|>A0(?STf%Jb= zU>CI33Mk52666=maPIt-YcB6BK}HyQx;TbNTux3<5Sr2EgBKR#8XADhvpj((~SKzFKEG znW_I}-`u_Do_o&kp8LD!p53>-c5O{wZc(nmV90a3oORGI(%QUi_#R6*u0Zz|^11zP z_xBC+U;viJZyAi54K(94Im4J~$OPSe&ggPpvfovi6y$KX#B7LYwGo+@4yDG0~KXwShmym^qr^C>oXrG8mHkw(N4aUac+cT326v=HS-F&RZGp zvK_tnBS&+qpUwYiSHt2pcVEr%6m=JVw`S<**w~xGUxto1HRd!{*=gL zSv@yf&K_txM;-RHh)+MY!LY(59k`Cm zfvl-X9MetIO%B`#iBuJdADYr6sc`jBKll)wr^NutGj2w82szy1IOe7Mf z1Z#@K0wis>+ewNc8HNB0LTL-De3A$&#TrBk!-*6zCP!5{62>%4UWmk12adx$HW6Pa z>hVm%hm|x7zz3P+qaT-59Jq9?lsN<63xbrvum4=cI@zOPns+kj%`8$t8PMwkT)P1q$XLr9ESGW#teON;C@h;=a; zvXT!@k4l4*0Lp?8YZh4xVFqWy%pi&ocq9>40a>l3G;i?>HXTY5xjQ1U5D&*Ghxh;@ zqv3!)p%Kni)VduwW1^-swLxC>gM$NKBZuS3sR^$fLiH-Iu}NE5ma;Hbnq}=)J5xGk z)PQ0NWTJ+t7f*dgI~EQ$1H|%Lo&tcbhrMuBF~qBpm^Ts$I`GM|J2~xv>m>0i@8nek zpp%8zJ5h*BD5%S+@HIG3M3Vh&|I1ptJXjgn!;!mW1=erVRjC_Qk2a@9sbNsouM&po zmxANP)D{%J6-j!WfR&mOTljDQLG?(d>x5qZnpCitGRR^T`~+eco-q4any?8lP zW!D^CvtnRY#&g;Af1@jR>U9c*;a^Y!UX}(v>RJOYTG_(t8mHm9_Vo5Y^B8F6M_o+{ z^xL#%q@G*66oeer?WxZBaDnkV1-Hc?x_Agwr`*m8uj}NeKR-M_qawfhde4dM&W^k% z9%$_Jb#BjU*`Md@_~X;rk8jLueG(IbIWI=vW%q8aTHpU;&$er4M&B8^%==n@(>$8H zD6?_=&W_^z`v(Kr%b%*h>q^G(Ctno(?H3y|e)y%bG4ijwv&xAdQj;{LbsT&r!>(2gebW8KH?&c4y-R<3PufpXkropym=kN~Y^dF88 zSNDGWGj8nB%p-<|hNWW#`O&^FetE*;eEn2sq0O+hy)5S=<96&?|B$_M8SKm8u3qcx ItK8W6Z%qmByZ`_I literal 0 HcmV?d00001 diff --git a/docs/img/settings_icon.png b/docs/img/settings_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..bf21c417c7817481296f471e0115634b5d7520d4 GIT binary patch literal 697 zcmV;q0!ICbP)Px%b4f%&R7gv;mRpETVHC%If29y9Ja}*kNs?#J9+cdg35m$1DYqJg7#&5%ZHSpD zifL-(lE`f|<;kNbNuJ20kRl3c4oWJSSsic)m>y3*4rMDzx?^T7z)s-0 zq$Sk?oCY=n>m+ql5<)Dc^V1?}SG52SfLXu-Np}kXJi8mfeMz$m0nBVMa1HnjjF$Ah z0Kk>@E(l^cP%r6b(*F+!X0{4g0nCjKjgn6JvzfVHDVEKDYLj%h?;DqZlBT2qdI3*&e309#R8zeNMgcQ| zvIYZ>fY*}7rTiuWFMwW2!}0(oNP3w%_y!moQlA+C9u@$6k>s3YmdHXTUZOpT^u6BR zxk=kEX0rTUbjgrnLNOnN4@i(9~MCfCrY-YASjun6H0 zGr-X>w$nUi?*lXIjt$p0?COC7QKpXf1Za}fWoFKWb13WGj0y{#}ozIG(Xk`usR+i=V@5*&E;!;tHhy z4_VMvy)Oso0lShQzhDLqPFW>o6;(BLO)XsmLt_&QOKTfjdj}T}FQ2mV>e|IimMveg za@Fd!>o;uNymRlq{RfWRxqI*agLfZ3fBE~*(rMBWph;&uT^vI+&i78b>BrHYubmjGweD_o{hz>*Dt+J-aTo_^U8bg#B)7^m^M0#*|RU3 zHs`%nh)QcVLqz-jly4gL9P9h`CLb)T@ibld^8AOVFMVEIKe~VaQwHH*%IIr;tHhy z4>j;Yd!HiE-PR>Re!&ckENmRSeEb4}Lc(J5DjGTlp5DF@F|qMUDH)mBIl1|TMWuCX zH|{ui_}GclXU<-{bou)2JFnh<`1t9o*Un3+Kx5WSqfOB)i0ljbc zs$B&x{`p_e8Rv3qcJe$UOXKSIX^r(BS(R$i=Qd|^85>5q#AF_snSSJ0XL{bG?D;dio&-dF dT3C0D`}#rklLwXF7z16-;OXk;vd$@?2>>rpGCu$S literal 0 HcmV?d00001 diff --git a/include/panda_qt/config_window.hpp b/include/panda_qt/config_window.hpp index 4a5238794..347a043bd 100644 --- a/include/panda_qt/config_window.hpp +++ b/include/panda_qt/config_window.hpp @@ -1,16 +1,25 @@ #pragma once #include +#include #include #include +#include #include +#include +#include #include #include +#include +#include +#include + +#include "emulator.hpp" class ConfigWindow : public QDialog { Q_OBJECT - private: + private: enum class Theme : int { System = 0, Light = 1, @@ -20,11 +29,20 @@ class ConfigWindow : public QDialog { }; Theme currentTheme; - QComboBox* themeSelect = nullptr; + QTextEdit* helpText = nullptr; + QListWidget* widgetList = nullptr; + QStackedWidget* widgetContainer = nullptr; + + static constexpr size_t settingWidgetCount = 6; + std::array helpTexts; + void addWidget(QWidget* widget, QString title, QString icon, QString helpText); void setTheme(Theme theme); - public: - ConfigWindow(QWidget* parent = nullptr); + public: + ConfigWindow(Emulator* emu, QWidget* parent = nullptr); ~ConfigWindow(); + + private: + Emulator* emu; }; diff --git a/src/panda_qt/config_window.cpp b/src/panda_qt/config_window.cpp index 75293742e..2220e25af 100644 --- a/src/panda_qt/config_window.cpp +++ b/src/panda_qt/config_window.cpp @@ -1,21 +1,364 @@ #include "panda_qt/config_window.hpp" -ConfigWindow::ConfigWindow(QWidget* parent) : QDialog(parent) { +ConfigWindow::ConfigWindow(Emulator* emu, QWidget* parent) : QDialog(parent), emu(emu) { setWindowTitle(tr("Configuration")); + EmulatorConfig& config = emu->getConfig(); + // Set up theme selection setTheme(Theme::Dark); - themeSelect = new QComboBox(this); + + // Initialize the widget list and the widget container widgets + widgetList = new QListWidget(this); + widgetContainer = new QStackedWidget(this); + + helpText = new QTextEdit(this); + helpText->setReadOnly(true); + + helpText->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + helpText->setFixedHeight(50); + + widgetList->setMinimumWidth(100); + widgetList->setMaximumWidth(100); + widgetList->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + widgetList->setPalette(QPalette(QColor(25, 25, 25))); + + widgetList->setCurrentRow(0); + widgetContainer->setCurrentIndex(0); + + connect(widgetList, &QListWidget::currentRowChanged, this, [&](int row) { + widgetContainer->setCurrentIndex(row); + helpText->setText(helpTexts[row]); + }); + + QVBoxLayout* mainLayout = new QVBoxLayout; + QHBoxLayout* hLayout = new QHBoxLayout; + + // Set up widget layouts + setLayout(mainLayout); + mainLayout->addLayout(hLayout); + mainLayout->addWidget(helpText); + + hLayout->setAlignment(Qt::AlignLeft); + hLayout->addWidget(widgetList); + hLayout->addWidget(widgetContainer); + + // Interface settings + QGroupBox *guiGroupBox = new QGroupBox(tr("Interface Settings"), this); + QFormLayout *guiLayout = new QFormLayout(guiGroupBox); + guiLayout->setHorizontalSpacing(20); + guiLayout->setVerticalSpacing(10); + + QComboBox *themeSelect = new QComboBox; themeSelect->addItem(tr("System")); themeSelect->addItem(tr("Light")); themeSelect->addItem(tr("Dark")); themeSelect->addItem(tr("Greetings Cat")); themeSelect->addItem(tr("Cream")); themeSelect->setCurrentIndex(static_cast(currentTheme)); + connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) { + setTheme(static_cast(index)); + }); + guiLayout->addRow(tr("Color theme"), themeSelect); + + QCheckBox *showAppVersion = new QCheckBox(tr("Show version on window title")); + showAppVersion->setChecked(config.windowSettings.showAppVersion); + connect(showAppVersion, &QCheckBox::toggled, this, [&](bool checked) { + config.windowSettings.showAppVersion = checked; + config.save(); + }); + guiLayout->addRow(showAppVersion); + + QCheckBox *rememberPosition = new QCheckBox(tr("Remember window position")); + rememberPosition->setChecked(config.windowSettings.rememberPosition); + connect(rememberPosition, &QCheckBox::toggled, this, [&](bool checked) { + config.windowSettings.rememberPosition = checked; + config.save(); + }); + guiLayout->addRow(rememberPosition); + + QSpinBox *windowPosX = new QSpinBox; + windowPosX->setMinimum(0); + windowPosX->setValue(config.windowSettings.x); + connect(windowPosX, &QSpinBox::valueChanged, this, [&](int value) { + config.windowSettings.x = static_cast(value); + config.save(); + }); + guiLayout->addRow(tr("Window X position"), windowPosX); + + QSpinBox *windowPosY = new QSpinBox; + windowPosY->setMinimum(0); + windowPosY->setValue(config.windowSettings.y); + connect(windowPosY, &QSpinBox::valueChanged, this, [&](int value) { + config.windowSettings.y = static_cast(value); + config.save(); + }); + guiLayout->addRow(tr("Window Y position"), windowPosY); + + QSpinBox *windowWidth = new QSpinBox; + windowWidth->setMinimum(0); + windowWidth->setValue(config.windowSettings.width); + connect(windowWidth, &QSpinBox::valueChanged, this, [&](int value) { + config.windowSettings.width = static_cast(value); + config.save(); + }); + guiLayout->addRow(tr("Window width"), windowWidth); + + QSpinBox *windowHeight = new QSpinBox; + windowHeight->setMinimum(0); + windowHeight->setValue(config.windowSettings.height); + connect(windowHeight, &QSpinBox::valueChanged, this, [&](int value) { + config.windowSettings.height = static_cast(value); + config.save(); + }); + guiLayout->addRow(tr("Window height"), windowHeight); + + // General settings + QGroupBox *genGroupBox = new QGroupBox(tr("General Settings"), this); + QFormLayout *genLayout = new QFormLayout(genGroupBox); + genLayout->setHorizontalSpacing(20); + genLayout->setVerticalSpacing(10); + + QLineEdit *defaultRomPath = new QLineEdit; + defaultRomPath->setText(config.defaultRomPath.c_str()); + connect(defaultRomPath, &QLineEdit::textChanged, this, [&](const QString &text) { + config.defaultRomPath = text.toStdString(); + config.save(); + }); + QPushButton *browseRomPath = new QPushButton(tr("Browse...")); + browseRomPath->setAutoDefault(false); + connect(browseRomPath, &QPushButton::pressed, this, [&, defaultRomPath]() { + QString newPath = QFileDialog::getExistingDirectory( + this, tr("Select Directory"), config.defaultRomPath.c_str(), + QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks + ); + if (!newPath.isEmpty()) { + defaultRomPath->setText(newPath); + } + }); + QHBoxLayout *romLayout = new QHBoxLayout; + romLayout->setSpacing(4); + romLayout->addWidget(defaultRomPath); + romLayout->addWidget(browseRomPath); + genLayout->addRow(tr("Default ROMs path"), romLayout); + + QCheckBox *discordRpcEnabled = new QCheckBox(tr("Enable Discord RPC")); + discordRpcEnabled->setChecked(config.discordRpcEnabled); + connect(discordRpcEnabled, &QCheckBox::toggled, this, [&](bool checked) { + config.discordRpcEnabled = checked; + config.save(); + }); + genLayout->addRow(discordRpcEnabled); + + QCheckBox *usePortableBuild = new QCheckBox(tr("Use portable build")); + usePortableBuild->setChecked(config.usePortableBuild); + connect(usePortableBuild, &QCheckBox::toggled, this, [&](bool checked) { + config.usePortableBuild = checked; + config.save(); + }); + genLayout->addRow(usePortableBuild); + + QCheckBox *printAppVersion = new QCheckBox(tr("Print version in console output")); + printAppVersion->setChecked(config.printAppVersion); + connect(printAppVersion, &QCheckBox::toggled, this, [&](bool checked) { + config.printAppVersion = checked; + config.save(); + }); + genLayout->addRow(printAppVersion); + + // Graphics settings + QGroupBox *gpuGroupBox = new QGroupBox(tr("Graphics Settings"), this); + QFormLayout *gpuLayout = new QFormLayout(gpuGroupBox); + gpuLayout->setHorizontalSpacing(20); + gpuLayout->setVerticalSpacing(10); + + QComboBox *rendererType = new QComboBox; + rendererType->addItem(tr("Null")); + rendererType->addItem(tr("OpenGL")); + rendererType->addItem(tr("Vulkan")); + rendererType->setCurrentIndex(static_cast(config.rendererType)); + connect(rendererType, &QComboBox::currentIndexChanged, this, [&](int index) { + config.rendererType = static_cast(index); + config.save(); + }); + gpuLayout->addRow(tr("GPU renderer"), rendererType); + + QCheckBox *enableRenderdoc = new QCheckBox(tr("Enable Renderdoc")); + enableRenderdoc->setChecked(config.enableRenderdoc); + connect(enableRenderdoc, &QCheckBox::toggled, this, [&](bool checked) { + config.enableRenderdoc = checked; + config.save(); + }); + gpuLayout->addRow(enableRenderdoc); + + QCheckBox *shaderJitEnabled = new QCheckBox(tr("Enable shader JIT")); + shaderJitEnabled->setChecked(config.shaderJitEnabled); + connect(shaderJitEnabled, &QCheckBox::toggled, this, [&](bool checked) { + config.shaderJitEnabled = checked; + config.save(); + }); + gpuLayout->addRow(shaderJitEnabled); - themeSelect->setGeometry(40, 40, 100, 50); - themeSelect->show(); - connect(themeSelect, &QComboBox::currentIndexChanged, this, [&](int index) { setTheme(static_cast(index)); }); + QCheckBox *vsyncEnabled = new QCheckBox(tr("Enable VSync")); + vsyncEnabled->setChecked(config.vsyncEnabled); + connect(vsyncEnabled, &QCheckBox::toggled, this, [&](bool checked) { + config.vsyncEnabled = checked; + config.save(); + }); + gpuLayout->addRow(vsyncEnabled); + + QCheckBox *useUbershaders = new QCheckBox(tr("Use ubershaders (No stutter, maybe slower)")); + useUbershaders->setChecked(config.useUbershaders); + connect(useUbershaders, &QCheckBox::toggled, this, [&](bool checked) { + config.useUbershaders = checked; + config.save(); + }); + gpuLayout->addRow(useUbershaders); + + QCheckBox *accurateShaderMul = new QCheckBox(tr("Accurate shader multiplication")); + accurateShaderMul->setChecked(config.accurateShaderMul); + connect(accurateShaderMul, &QCheckBox::toggled, this, [&](bool checked) { + config.accurateShaderMul = checked; + config.save(); + }); + gpuLayout->addRow(accurateShaderMul); + + QCheckBox *accelerateShaders = new QCheckBox(tr("Accelerate shaders")); + accelerateShaders->setChecked(config.accelerateShaders); + connect(accelerateShaders, &QCheckBox::toggled, this, [&](bool checked) { + config.accelerateShaders = checked; + config.save(); + }); + gpuLayout->addRow(accelerateShaders); + + QCheckBox *forceShadergenForLights = new QCheckBox(tr("Force shadergen when rendering lights")); + connect(forceShadergenForLights, &QCheckBox::toggled, this, [&](bool checked) { + config.forceShadergenForLights = checked; + config.save(); + }); + gpuLayout->addRow(forceShadergenForLights); + + QSpinBox *lightShadergenThreshold = new QSpinBox; + lightShadergenThreshold->setRange(1, 8); + lightShadergenThreshold->setValue(config.lightShadergenThreshold); + connect(lightShadergenThreshold, &QSpinBox::valueChanged, this, [&](int value) { + config.lightShadergenThreshold = static_cast(value); + config.save(); + }); + gpuLayout->addRow(tr("Light threshold for forcing shadergen"), lightShadergenThreshold); + + // Audio settings + QGroupBox *spuGroupBox = new QGroupBox(tr("Audio Settings"), this); + QFormLayout *spuLayout = new QFormLayout(spuGroupBox); + spuLayout->setHorizontalSpacing(20); + spuLayout->setVerticalSpacing(10); + + QComboBox *dspType = new QComboBox; + dspType->addItem(tr("Null")); + dspType->addItem(tr("LLE")); + dspType->addItem(tr("HLE")); + dspType->setCurrentIndex(static_cast(config.dspType)); + connect(dspType, &QComboBox::currentIndexChanged, this, [&](int index) { + config.dspType = static_cast(index); + config.save(); + }); + spuLayout->addRow(tr("DSP emulation"), dspType); + + QCheckBox *audioEnabled = new QCheckBox(tr("Enable audio")); + audioEnabled->setChecked(config.audioEnabled); + connect(audioEnabled, &QCheckBox::toggled, this, [&](bool checked) { + config.audioEnabled = checked; + config.save(); + }); + spuLayout->addRow(audioEnabled); + + QCheckBox *aacEnabled = new QCheckBox(tr("Enable AAC audio")); + aacEnabled->setChecked(config.aacEnabled); + connect(aacEnabled, &QCheckBox::toggled, this, [&](bool checked) { + config.aacEnabled = checked; + config.save(); + }); + spuLayout->addRow(aacEnabled); + + QCheckBox *printDSPFirmware = new QCheckBox(tr("Print DSP firmware")); + printDSPFirmware->setChecked(config.printDSPFirmware); + connect(printDSPFirmware, &QCheckBox::toggled, this, [&](bool checked) { + config.printDSPFirmware = checked; + config.save(); + }); + spuLayout->addRow(printDSPFirmware); + + QCheckBox *muteAudio = new QCheckBox(tr("Mute audio device")); + muteAudio->setChecked(config.audioDeviceConfig.muteAudio); + connect(muteAudio, &QCheckBox::toggled, this, [&](bool checked) { + config.audioDeviceConfig.muteAudio = checked; + config.save(); + }); + spuLayout->addRow(muteAudio); + + QSpinBox *volumeRaw = new QSpinBox; + volumeRaw->setRange(0, 200); + volumeRaw->setValue(config.audioDeviceConfig.volumeRaw * 100); + connect(volumeRaw, &QSpinBox::valueChanged, this, [&](int value) { + config.audioDeviceConfig.volumeRaw = static_cast(value) / 100; + config.save(); + }); + spuLayout->addRow(tr("Audio device volume"), volumeRaw); + + // Battery settings + QGroupBox *batGroupBox = new QGroupBox(tr("Battery Settings"), this); + QFormLayout *batLayout = new QFormLayout(batGroupBox); + batLayout->setHorizontalSpacing(20); + batLayout->setVerticalSpacing(10); + + QSpinBox *batteryPercentage = new QSpinBox; + batteryPercentage->setRange(1, 100); + batteryPercentage->setValue(config.batteryPercentage); + connect(batteryPercentage, &QSpinBox::valueChanged, this, [&](int value) { + config.batteryPercentage = static_cast(value); + config.save(); + }); + batLayout->addRow(tr("Battery percentage"), batteryPercentage); + + QCheckBox *chargerPlugged = new QCheckBox(tr("Charger plugged")); + chargerPlugged->setChecked(config.chargerPlugged); + connect(chargerPlugged, &QCheckBox::toggled, this, [&](bool checked) { + config.chargerPlugged = checked; + config.save(); + }); + batLayout->addRow(chargerPlugged); + + // SD Card settings + QGroupBox *sdcGroupBox = new QGroupBox(tr("SD Card Settings"), this); + QFormLayout *sdcLayout = new QFormLayout(sdcGroupBox); + sdcLayout->setHorizontalSpacing(20); + sdcLayout->setVerticalSpacing(10); + + QCheckBox *sdCardInserted = new QCheckBox(tr("Enable virtual SD card")); + sdCardInserted->setChecked(config.sdCardInserted); + connect(sdCardInserted, &QCheckBox::toggled, this, [&](bool checked) { + config.sdCardInserted = checked; + config.save(); + }); + sdcLayout->addRow(sdCardInserted); + + QCheckBox *sdWriteProtected = new QCheckBox(tr("Write protect virtual SD card")); + sdWriteProtected->setChecked(config.sdWriteProtected); + connect(sdWriteProtected, &QCheckBox::toggled, this, [&](bool checked) { + config.sdWriteProtected = checked; + config.save(); + }); + sdcLayout->addRow(sdWriteProtected); + + // Add all our settings widgets to our widget list + addWidget(guiGroupBox, tr("Interface"), ":/docs/img/sparkling_icon.png", tr("User Interface settings")); + addWidget(genGroupBox, tr("General"), ":/docs/img/settings_icon.png", tr("General emulator settings")); + addWidget(gpuGroupBox, tr("Graphics"), ":/docs/img/display_icon.png", tr("Graphics emulation and output settings")); + addWidget(spuGroupBox, tr("Audio"), ":/docs/img/speaker_icon.png", tr("Audio emulation and output settings")); + addWidget(batGroupBox, tr("Battery"), ":/docs/img/battery_icon.png", tr("Battery emulation settings")); + addWidget(sdcGroupBox, tr("SD Card"), ":/docs/img/sdcard_icon.png", tr("SD Card emulation settings")); + + widgetList->setCurrentRow(0); } void ConfigWindow::setTheme(Theme theme) { @@ -119,4 +462,25 @@ void ConfigWindow::setTheme(Theme theme) { } } -ConfigWindow::~ConfigWindow() { delete themeSelect; } +void ConfigWindow::addWidget(QWidget* widget, QString title, QString icon, QString helpText) { + const int index = widgetList->count(); + + QListWidgetItem* item = new QListWidgetItem(widgetList); + item->setText(title); + if (!icon.isEmpty()) { + item->setIcon(QIcon::fromTheme(icon)); + } + + widgetContainer->addWidget(widget); + + if (index >= settingWidgetCount) { + Helpers::panic("Qt: ConfigWindow::settingWidgetCount has not been updated correctly!"); + } + helpTexts[index] = std::move(helpText); +} + +ConfigWindow::~ConfigWindow() { + delete helpText; + delete widgetList; + delete widgetContainer; +} diff --git a/src/panda_qt/main_window.cpp b/src/panda_qt/main_window.cpp index 93ce26133..5eead6869 100644 --- a/src/panda_qt/main_window.cpp +++ b/src/panda_qt/main_window.cpp @@ -81,7 +81,7 @@ MainWindow::MainWindow(QApplication* app, QWidget* parent) : QMainWindow(parent) // Set up misc objects aboutWindow = new AboutWindow(nullptr); - configWindow = new ConfigWindow(this); + configWindow = new ConfigWindow(emu, this); cheatsEditor = new CheatsWindow(emu, {}, this); patchWindow = new PatchWindow(this); luaEditor = new TextEditorWindow(this, "script.lua", "");