Move GameBoyConfig dialog to its own class

This changes all of the options handled by the GameBoyConfig dialog to
use the new Option type. This also fixes a number of small issues
related to palette handling and GB emulation:
* Modifying the custom GB palette no longer incorrectly applies to GBC
  and SGB modes.
* Loading a GB save state (not GBC or SGB) properly applies the
  user-defined palettte and not the one in the save state.
* The GBC core now accepts .gbc BIOS files.

Finally, this modifies and renames wxFarRadio to widgets::GroupCheckBox.
In addition GroupCheckBoxes can now be loaded and fully configured from
the XRC file rather than having to be manually configured on dialog
initialization.
This commit is contained in:
Fabrice de Gans 2023-03-14 11:47:34 -07:00 committed by Rafael Kitover
parent 1d7e8ae4ed
commit 9889ef4fa8
32 changed files with 1217 additions and 1060 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,17 @@
//#include "../win32/stdafx.h" // would fix LNK2005 linker errors for MSVC
#include "gb.h"
#include <array>
#include <cassert>
#include <cmath>
#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include "../NLS.h"
#include "../System.h"
#include "../Util.h"
#include "../gba/GBALink.h"
#include "../gba/Sound.h"
#include "gb.h"
#include "gbCheats.h"
#include "gbGlobals.h"
#include "gbMemory.h"
@ -22,6 +22,45 @@
#define _stricmp strcasecmp
#endif
namespace {
// These are the default palettes when launching a GB game for GBC, GBA and
// GBA SP, respectively.
static constexpr std::array<uint16_t, 0x40> kGbGbcPalette = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x0600, 0xfdf3, 0x041c, 0xf5db, 0x4419, 0x57ea, 0x2808, 0x9b75,
0x129b, 0xfce0, 0x22da, 0x4ac5, 0x2d71, 0xf0c2, 0x5137, 0x2d41,
0x6b2d, 0x2215, 0xbe0a, 0xc053, 0xfe5f, 0xe000, 0xbe10, 0x914d,
0x7f91, 0x02b5, 0x77ac, 0x14e5, 0xcf89, 0xa03d, 0xfd50, 0x91ff,
};
static constexpr std::array<uint16_t, 0x40> kGbGbaPalette = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0xbe00, 0xfdfd, 0xbd69, 0x7baf, 0xf5ff, 0x3f8f, 0xcee5, 0x5bf7,
0xb35b, 0xef97, 0xef9f, 0x97f7, 0x82bf, 0x9f3d, 0xddde, 0xbad5,
0x3cba, 0xdfd7, 0xedea, 0xfeda, 0xf7f9, 0xfdee, 0x6d2f, 0xf0e6,
0xf7f0, 0xf296, 0x3bf1, 0xe211, 0x69ba, 0x3d0d, 0xdfd3, 0xa6ba,
};
static constexpr std::array<uint16_t, 0x40> kGbGbaSpPalette = {
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff, 0x7fff,
0x9c00, 0x6340, 0x10c6, 0xdb97, 0x7622, 0x3e57, 0x2e12, 0x95c3,
0x1095, 0x488c, 0x8241, 0xde8c, 0xfabc, 0x0e81, 0x7675, 0xfdec,
0xddfd, 0x5995, 0x066a, 0xed1e, 0x1e84, 0x1d14, 0x11c3, 0x2749,
0xa727, 0x6266, 0xe27b, 0xe3fc, 0x1f76, 0xf158, 0x468e, 0xa540,
};
} // namespace
extern uint8_t* pix;
bool gbUpdateSizes();
bool inBios = false;
@ -2184,6 +2223,8 @@ bool CPUIsGBBios(const char* file)
const char* p = strrchr(file, '.');
if (p != NULL) {
if (_stricmp(p, ".gbc") == 0)
return true;
if (_stricmp(p, ".gb") == 0)
return true;
if (_stricmp(p, ".bin") == 0)
@ -2315,6 +2356,14 @@ static void gbSelectColorizationPalette()
memcpy(gbPalette, gbColorizationPaletteData[palette][2], sizeof(gbColorizationPaletteData[palette][0]));
}
void gbResetPalette() {
if (!gbCgbMode && !gbSgbMode) {
// In SGB Mode, palette initialization is done in gbSgbReset().
memcpy(gbPalette, &systemGbPalette[gbPaletteOption * 8],
8 * sizeof(systemGbPalette[0]));
}
}
void gbReset()
{
#ifndef NO_LINK
@ -2606,10 +2655,9 @@ void gbReset()
gbTimerOnChange = false;
gbTimerOn = 0;
if (gbCgbMode) {
for (i = 0; i < 0x20; i++)
gbPalette[i] = 0x7fff;
gbResetPalette();
if (gbCgbMode) {
// This is just to show that the starting values of the OBJ palettes are different
// between the 3 consoles, and that they 'kinda' stay the same at each reset
// (they can slightly change, somehow (randomly?)).
@ -2618,122 +2666,21 @@ void gbReset()
// is running (GB,GBC and GBA(SP) have different startup values).
// Unfortunatly, I don't have any SGB system, so I can't get their starting values.
if (gbGBCColorType == 0) // GBC Hardware
{
gbPalette[0x20] = 0x0600;
gbPalette[0x21] = 0xfdf3;
gbPalette[0x22] = 0x041c;
gbPalette[0x23] = 0xf5db;
gbPalette[0x24] = 0x4419;
gbPalette[0x25] = 0x57ea;
gbPalette[0x26] = 0x2808;
gbPalette[0x27] = 0x9b75;
gbPalette[0x28] = 0x129b;
gbPalette[0x29] = 0xfce0;
gbPalette[0x2a] = 0x22da;
gbPalette[0x2b] = 0x4ac5;
gbPalette[0x2c] = 0x2d71;
gbPalette[0x2d] = 0xf0c2;
gbPalette[0x2e] = 0x5137;
gbPalette[0x2f] = 0x2d41;
gbPalette[0x30] = 0x6b2d;
gbPalette[0x31] = 0x2215;
gbPalette[0x32] = 0xbe0a;
gbPalette[0x33] = 0xc053;
gbPalette[0x34] = 0xfe5f;
gbPalette[0x35] = 0xe000;
gbPalette[0x36] = 0xbe10;
gbPalette[0x37] = 0x914d;
gbPalette[0x38] = 0x7f91;
gbPalette[0x39] = 0x02b5;
gbPalette[0x3a] = 0x77ac;
gbPalette[0x3b] = 0x14e5;
gbPalette[0x3c] = 0xcf89;
gbPalette[0x3d] = 0xa03d;
gbPalette[0x3e] = 0xfd50;
gbPalette[0x3f] = 0x91ff;
} else if (gbGBCColorType == 1) // GBA Hardware
{
gbPalette[0x20] = 0xbe00;
gbPalette[0x21] = 0xfdfd;
gbPalette[0x22] = 0xbd69;
gbPalette[0x23] = 0x7baf;
gbPalette[0x24] = 0xf5ff;
gbPalette[0x25] = 0x3f8f;
gbPalette[0x26] = 0xcee5;
gbPalette[0x27] = 0x5bf7;
gbPalette[0x28] = 0xb35b;
gbPalette[0x29] = 0xef97;
gbPalette[0x2a] = 0xef9f;
gbPalette[0x2b] = 0x97f7;
gbPalette[0x2c] = 0x82bf;
gbPalette[0x2d] = 0x9f3d;
gbPalette[0x2e] = 0xddde;
gbPalette[0x2f] = 0xbad5;
gbPalette[0x30] = 0x3cba;
gbPalette[0x31] = 0xdfd7;
gbPalette[0x32] = 0xedea;
gbPalette[0x33] = 0xfeda;
gbPalette[0x34] = 0xf7f9;
gbPalette[0x35] = 0xfdee;
gbPalette[0x36] = 0x6d2f;
gbPalette[0x37] = 0xf0e6;
gbPalette[0x38] = 0xf7f0;
gbPalette[0x39] = 0xf296;
gbPalette[0x3a] = 0x3bf1;
gbPalette[0x3b] = 0xe211;
gbPalette[0x3c] = 0x69ba;
gbPalette[0x3d] = 0x3d0d;
gbPalette[0x3e] = 0xdfd3;
gbPalette[0x3f] = 0xa6ba;
} else if (gbGBCColorType == 2) // GBASP Hardware
{
gbPalette[0x20] = 0x9c00;
gbPalette[0x21] = 0x6340;
gbPalette[0x22] = 0x10c6;
gbPalette[0x23] = 0xdb97;
gbPalette[0x24] = 0x7622;
gbPalette[0x25] = 0x3e57;
gbPalette[0x26] = 0x2e12;
gbPalette[0x27] = 0x95c3;
gbPalette[0x28] = 0x1095;
gbPalette[0x29] = 0x488c;
gbPalette[0x2a] = 0x8241;
gbPalette[0x2b] = 0xde8c;
gbPalette[0x2c] = 0xfabc;
gbPalette[0x2d] = 0x0e81;
gbPalette[0x2e] = 0x7675;
gbPalette[0x2f] = 0xfdec;
gbPalette[0x30] = 0xddfd;
gbPalette[0x31] = 0x5995;
gbPalette[0x32] = 0x066a;
gbPalette[0x33] = 0xed1e;
gbPalette[0x34] = 0x1e84;
gbPalette[0x35] = 0x1d14;
gbPalette[0x36] = 0x11c3;
gbPalette[0x37] = 0x2749;
gbPalette[0x38] = 0xa727;
gbPalette[0x39] = 0x6266;
gbPalette[0x3a] = 0xe27b;
gbPalette[0x3b] = 0xe3fc;
gbPalette[0x3c] = 0x1f76;
gbPalette[0x3d] = 0xf158;
gbPalette[0x3e] = 0x468e;
gbPalette[0x3f] = 0xa540;
if (gbGBCColorType == 0) {
// GBC Hardware
std::copy(kGbGbcPalette.begin(), kGbGbcPalette.end(), gbPalette);
} else if (gbGBCColorType == 1) {
// GBA Hardware
std::copy(kGbGbaPalette.begin(), kGbGbaPalette.end(), gbPalette);
} else if (gbGBCColorType == 2) {
// GBASP Hardware
std::copy(kGbGbaSpPalette.begin(), kGbGbaSpPalette.end(), gbPalette);
}
// The CGB BIOS palette selection has to be done by VBA if BIOS is skipped.
if (!(gbRom[0x143] & 0x80) && !inBios) {
gbSelectColorizationPalette();
}
} else {
if (gbSgbMode) {
for (i = 0; i < 8; i++)
gbPalette[i] = systemGbPalette[gbPaletteOption * 8 + i];
}
for (i = 0; i < 8; i++)
gbPalette[i] = systemGbPalette[gbPaletteOption * 8 + i];
}
GBTIMER_MODE_0_CLOCK_TICKS = 256;
@ -3985,12 +3932,9 @@ static bool gbReadSaveState(gzFile gzFile)
if (version < 11)
utilGzRead(gzFile, gbPalette, 128 * sizeof(uint16_t));
if (version < GBSAVE_GAME_VERSION_10) {
if (!gbCgbMode && !gbSgbMode) {
for (int i = 0; i < 8; i++)
gbPalette[i] = systemGbPalette[gbPaletteOption * 8 + i];
}
}
// This is necessary for GB games (not GBC or SGB) to have them load with
// the user-defined palette and not the saved palette.
gbResetPalette();
utilGzRead(gzFile, &gbMemory[0x8000], 0x8000);

View File

@ -1,6 +1,8 @@
#ifndef GB_H
#define GB_H
#include <cstdint>
#define gbWidth 160
#define gbHeight 144
#define sgbWidth 256
@ -33,6 +35,12 @@ void gbWriteMemory(uint16_t, uint8_t);
void gbDrawLine();
bool gbIsGameboyRom(const char*);
void gbGetHardwareType();
// Resets gbPalette to systemGbPalette and gbPaletteOption value. This is called
// in gbReset and only needs to be called when systemGbPalette or
// gbPaletteOption is updated while a GB game is running.
void gbResetPalette();
void gbReset();
void gbCleanUp();
void gbCPUInit(const char*, bool);

View File

@ -27,14 +27,14 @@ bool genericflashcardEnable = false;
int gbCgbMode = 0;
uint16_t gbColorFilter[32768];
int gbColorOption = 0;
int gbPaletteOption = 0;
int gbEmulatorType = 0;
int gbBorderOn = 0;
int gbBorderAutomatic = 0;
uint32_t gbEmulatorType = 0;
uint32_t gbPaletteOption = 0;
int gbBorderLineSkip = 160;
int gbBorderRowSkip = 0;
int gbBorderColumnSkip = 0;
int gbDmaTicks = 0;
bool gbBorderAutomatic = false;
bool gbBorderOn = false;
bool gbColorOption = false;
uint8_t (*gbSerialFunction)(uint8_t) = NULL;

View File

@ -23,11 +23,8 @@ extern uint8_t* gbMemoryMap[16];
extern int gbFrameSkip;
extern uint16_t gbColorFilter[32768];
extern int gbColorOption;
extern int gbPaletteOption;
extern int gbEmulatorType;
extern int gbBorderOn;
extern int gbBorderAutomatic;
extern uint32_t gbEmulatorType;
extern uint32_t gbPaletteOption;
extern int gbCgbMode;
extern int gbSgbMode;
extern int gbWindowLine;
@ -36,8 +33,10 @@ extern uint8_t gbBgp[4];
extern uint8_t gbObp0[4];
extern uint8_t gbObp1[4];
extern uint16_t gbPalette[128];
extern bool gbBorderAutomatic;
extern bool gbBorderOn;
extern bool gbColorOption;
extern bool gbScreenOn;
extern bool gbDrawWindow;
extern uint8_t gbSCYLine[300];
// gbSCXLine is used for the emulation (bug) of the SX change
// found in the Artic Zone game.

View File

@ -319,7 +319,7 @@ void gbSgbPicture()
gbSgbCGBSupport |= 4;
if (gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) {
gbBorderOn = 1;
gbBorderOn = true;
systemGbBorderOn();
}
@ -663,7 +663,7 @@ void gbSgbChrTransfer()
memcpy(&gbSgbBorderChar[address], gbSgbScreenBuffer, 128 * 32);
if (gbBorderAutomatic && !gbBorderOn && gbSgbCGBSupport > 4) {
gbBorderOn = 1;
gbBorderOn = true;
systemGbBorderOn();
}

View File

@ -1067,14 +1067,14 @@ static void update_variables(bool startup)
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
if (strcmp(var.value, "auto") == 0) {
gbBorderAutomatic = 1;
gbBorderAutomatic = true;
}
else if (!strcmp(var.value, "enabled")) {
gbBorderAutomatic = 0;
gbBorderOn = 1;
gbBorderAutomatic = false;
gbBorderOn = false;
} else { // disabled
gbBorderOn = 0;
gbBorderAutomatic = 0;
gbBorderOn = true;
gbBorderAutomatic = false;
}
if ((type == IMAGE_GB) && !startup)
@ -1179,7 +1179,7 @@ static void update_variables(bool startup)
var.value = NULL;
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value) {
gbColorOption = (!strcmp(var.value, "enabled")) ? 1 : 0;
gbColorOption = (!strcmp(var.value, "enabled"));
}
var.key = "vbam_lcdfilter";

View File

@ -69,6 +69,9 @@ enum named_opts
OPT_CPU_SAVE_TYPE,
OPT_DOTCODE_FILE_NAME_LOAD,
OPT_DOTCODE_FILE_NAME_SAVE,
OPT_GB_BORDER_AUTOMATIC,
OPT_GB_BORDER_ON,
OPT_GB_COLOR_OPTION,
OPT_GB_EMULATOR_TYPE,
OPT_GB_FRAME_SKIP,
OPT_GB_PALETTE_OPTION,
@ -147,12 +150,12 @@ struct option argOptions[] = {
{ "bios-file-name-gb", required_argument, 0, OPT_BIOS_FILE_NAME_GB },
{ "bios-file-name-gba", required_argument, 0, OPT_BIOS_FILE_NAME_GBA },
{ "bios-file-name-gbc", required_argument, 0, OPT_BIOS_FILE_NAME_GBC },
{ "border-automatic", no_argument, &gbBorderAutomatic, 1 },
{ "border-on", no_argument, &gbBorderOn, 1 },
{ "border-automatic", no_argument, 0, OPT_GB_BORDER_AUTOMATIC },
{ "border-on", no_argument, 0, OPT_GB_BORDER_ON },
{ "capture-format", required_argument, 0, OPT_CAPTURE_FORMAT },
{ "cheat", required_argument, 0, OPT_CHEAT },
{ "cheats-enabled", no_argument, &coreOptions.cheatsEnabled, 1 },
{ "color-option", no_argument, &gbColorOption, 1 },
{ "color-option", no_argument, 0, OPT_GB_COLOR_OPTION },
{ "config", required_argument, 0, 'c' },
{ "cpu-disable-sfx", no_argument, &coreOptions.cpuDisableSfx, 1 },
{ "cpu-save-type", required_argument, 0, OPT_CPU_SAVE_TYPE },
@ -167,9 +170,9 @@ struct option argOptions[] = {
{ "flash-size", required_argument, 0, 'S' },
{ "frameskip", required_argument, 0, 's' },
{ "full-screen", no_argument, &fullScreen, 1 },
{ "gb-border-automatic", no_argument, &gbBorderAutomatic, 1 },
{ "gb-border-on", no_argument, &gbBorderOn, 1 },
{ "gb-color-option", no_argument, &gbColorOption, 1 },
{ "gb-border-automatic", no_argument, 0, OPT_GB_BORDER_AUTOMATIC },
{ "gb-border-on", no_argument, 0, OPT_GB_BORDER_ON },
{ "gb-color-option", no_argument, 0, OPT_GB_COLOR_OPTION },
{ "gb-emulator-type", required_argument, 0, OPT_GB_EMULATOR_TYPE },
{ "gb-frame-skip", required_argument, 0, OPT_GB_FRAME_SKIP },
{ "gb-palette-option", required_argument, 0, OPT_GB_PALETTE_OPTION },
@ -257,8 +260,10 @@ void OpenPreferences(const char *name)
void ValidateConfig()
{
if (gbEmulatorType < 0 || gbEmulatorType > 5)
if (gbEmulatorType > 5)
gbEmulatorType = 1;
if (gbPaletteOption > 2)
gbPaletteOption = 0;
if (frameSkip < 0 || frameSkip > 9)
frameSkip = 2;
if (gbFrameSkip < 0 || gbFrameSkip > 9)
@ -863,6 +868,24 @@ int ReadOpts(int argc, char ** argv)
}
break;
case OPT_GB_BORDER_AUTOMATIC:
// --border-automatic
// --gb-border-automatic
gbBorderAutomatic = true;
break;
case OPT_GB_BORDER_ON:
// --border-on
// --gb-border-on
gbBorderOn = true;
break;
case OPT_GB_COLOR_OPTION:
// --color-option
// --gb-color-option
gbColorOption = true;
break;
case OPT_GB_EMULATOR_TYPE:
// --gb-emulator-type
if (optarg) {

View File

@ -1562,7 +1562,7 @@ int main(int argc, char** argv)
SetHomeDataDir();
frameSkip = 2;
gbBorderOn = 0;
gbBorderOn = false;
coreOptions.parseDebug = true;

View File

@ -765,6 +765,8 @@ set(
config/option.cpp
config/user-input.cpp
dialogs/display-config.cpp
dialogs/game-boy-config.cpp
widgets/group-check-box.cpp
widgets/keep-on-top-styler.cpp
widgets/keyedit.cpp
widgets/joyedit.cpp
@ -811,7 +813,10 @@ set(
config/option.h
config/user-input.h
dialogs/display-config.h
dialogs/game-boy-config.h
dialogs/validated-child.h
widgets/dpi-support.h
widgets/group-check-box.h
widgets/keep-on-top-styler.h
widgets/option-validator.h
widgets/render-plugin.h

View File

@ -2492,50 +2492,7 @@ EVT_HANDLER(UIConfigure, "UI Settings...")
EVT_HANDLER(GameBoyConfigure, "Game Boy options...")
{
wxDialog* dlg = GetXRCDialog("GameBoyConfig");
wxChoice* c = XRCCTRL(*dlg, "Borders", wxChoice);
bool borderon = gbBorderOn;
if (!gbBorderOn && !gbBorderAutomatic)
c->SetSelection(0);
else if (gbBorderOn)
c->SetSelection(1);
else
c->SetSelection(2);
if (ShowModal(dlg) != wxID_OK)
return;
switch (c->GetSelection()) {
case 0:
gbBorderOn = gbBorderAutomatic = false;
break;
case 1:
gbBorderOn = true;
break;
case 2:
gbBorderOn = false;
gbBorderAutomatic = true;
break;
}
// this value might have been overwritten by FrameSkip
if (panel->game_type() == IMAGE_GB) {
if (borderon != gbBorderOn) {
if (gbBorderOn) {
panel->AddBorder();
gbSgbRenderBorder();
} else
panel->DelBorder();
}
// don't want to have to reset to change colors
memcpy(gbPalette, &systemGbPalette[gbPaletteOption * 8], 8 * sizeof(systemGbPalette[0]));
}
update_opts();
ShowModal(GetXRCDialog("GameBoyConfig"));
}
EVT_HANDLER(SetSize1x, "1x")
@ -3027,13 +2984,7 @@ EVT_HANDLER_MASK(GBLcdFilter, "Enable LCD filter", CMDEN_GB)
EVT_HANDLER(GBColorOption, "Enable GB color option")
{
bool menuPress = false;
bool intVar = gbColorOption ? true : false;
GetMenuOptionBool("GBColorOption", &menuPress);
toggleBooleanVar(&menuPress, &intVar);
SetMenuOption("GBColorOption", intVar ? 1 : 0);
gbColorOption = intVar ? 1 : 0;
update_opts();
GetMenuOptionConfig("GBColorOption", config::OptionID::kGBColorOption);
}
EVT_HANDLER(ApplyPatches, "Apply IPS/UPS/IPF patches if found")

View File

@ -162,7 +162,9 @@ std::array<Option, kNbOptions>& Option::All() {
bool retain_aspect = true;
/// GB
wxString gb_bios = wxEmptyString;
bool colorizer_hack = false;
wxString gbc_bios = wxEmptyString;
/// Core
bool agb_print = false;
@ -212,11 +214,11 @@ std::array<Option, kNbOptions>& Option::All() {
Option(OptionID::kDispStretch, &g_owned_opts.retain_aspect),
/// GB
Option(OptionID::kGBBiosFile, &gopts.gb_bios),
Option(OptionID::kGBColorOption, &gbColorOption, 0, 1),
Option(OptionID::kGBBiosFile, &g_owned_opts.gb_bios),
Option(OptionID::kGBColorOption, &gbColorOption),
Option(OptionID::kGBColorizerHack, &g_owned_opts.colorizer_hack),
Option(OptionID::kGBLCDFilter, &gopts.gb_lcd_filter),
Option(OptionID::kGBGBCBiosFile, &gopts.gbc_bios),
Option(OptionID::kGBGBCBiosFile, &g_owned_opts.gbc_bios),
Option(OptionID::kGBPalette0, systemGbPalette),
Option(OptionID::kGBPalette1, systemGbPalette + 8),
Option(OptionID::kGBPalette2, systemGbPalette + 16),
@ -264,8 +266,8 @@ std::array<Option, kNbOptions>& Option::All() {
Option(OptionID::kPrefAutoFrameSkip, &g_owned_opts.auto_frame_skip),
Option(OptionID::kPrefAutoPatch, &g_owned_opts.auto_patch),
Option(OptionID::kPrefAutoSaveLoadCheatList, &gopts.autoload_cheats),
Option(OptionID::kPrefBorderAutomatic, &gbBorderAutomatic, 0, 1),
Option(OptionID::kPrefBorderOn, &gbBorderOn, 0, 1),
Option(OptionID::kPrefBorderAutomatic, &gbBorderAutomatic),
Option(OptionID::kPrefBorderOn, &gbBorderOn),
Option(OptionID::kPrefCaptureFormat, &g_owned_opts.capture_format, 0, 1),
Option(OptionID::kPrefCheatsEnabled, &coreOptions.cheatsEnabled, 0, 1),
Option(OptionID::kPrefDisableStatus, &g_owned_opts.disable_status_messages),

View File

@ -23,7 +23,7 @@ static constexpr std::array<Option::Type, kNbOptions> kOptionsTypes = {
/// GB
/*kGBBiosFile*/ Option::Type::kString,
/*kGBColorOption*/ Option::Type::kInt,
/*kGBColorOption*/ Option::Type::kBool,
/*kGBColorizerHack*/ Option::Type::kBool,
/*kGBLCDFilter*/ Option::Type::kBool,
/*kGBGBCBiosFile*/ Option::Type::kString,
@ -74,15 +74,15 @@ static constexpr std::array<Option::Type, kNbOptions> kOptionsTypes = {
/*kPrefAutoFrameSkip*/ Option::Type::kBool,
/*kPrefAutoPatch*/ Option::Type::kBool,
/*kPrefAutoSaveLoadCheatList*/ Option::Type::kBool,
/*kPrefBorderAutomatic*/ Option::Type::kInt,
/*kPrefBorderOn*/ Option::Type::kInt,
/*kPrefBorderAutomatic*/ Option::Type::kBool,
/*kPrefBorderOn*/ Option::Type::kBool,
/*kPrefCaptureFormat*/ Option::Type::kUnsigned,
/*kPrefCheatsEnabled*/ Option::Type::kInt,
/*kPrefDisableStatus*/ Option::Type::kBool,
/*kPrefEmulatorType*/ Option::Type::kInt,
/*kPrefEmulatorType*/ Option::Type::kUnsigned,
/*kPrefFlashSize*/ Option::Type::kUnsigned,
/*kPrefFrameSkip*/ Option::Type::kInt,
/*kPrefGBPaletteOption*/ Option::Type::kInt,
/*kPrefGBPaletteOption*/ Option::Type::kUnsigned,
/*kPrefGBPrinter*/ Option::Type::kInt,
/*kPrefGDBBreakOnLoad*/ Option::Type::kBool,
/*kPrefGDBPort*/ Option::Type::kInt,

View File

@ -1,5 +1,6 @@
#include "config/option.h"
#include <cstring>
#include "nonstd/variant.hpp"
#include <wx/log.h>
@ -287,6 +288,15 @@ wxString Option::GetEnumString() const {
return wxEmptyString;
}
std::array<uint16_t, 8> Option::GetGbPalette() const {
assert(is_gb_palette());
const uint16_t* raw_palette = (nonstd::get<uint16_t*>(value_));
std::array<uint16_t, 8> palette;
std::memcpy(palette.data(), raw_palette, sizeof(palette));
return palette;
}
wxString Option::GetGbPaletteString() const {
assert(is_gb_palette());
@ -454,7 +464,25 @@ bool Option::SetEnumInt(int value) {
return true;
}
bool Option::SetGbPalette(const wxString& value) {
bool Option::SetGbPalette(const std::array<uint16_t, 8>& value) {
assert(is_gb_palette());
uint16_t* dest = nonstd::get<uint16_t*>(value_);
// Keep a copy of the current value.
std::array<uint16_t, 8> old_value;
std::copy(dest, dest + 8, old_value.data());
// Set the new value.
std::copy(value.begin(), value.end(), dest);
if (old_value != value) {
CallObservers();
}
return true;
}
bool Option::SetGbPaletteString(const wxString& value) {
assert(is_gb_palette());
// 8 values of 4 chars and 7 commas.
@ -465,10 +493,6 @@ bool Option::SetGbPalette(const wxString& value) {
return false;
}
uint16_t* dest = nonstd::get<uint16_t*>(value_);
std::array<uint16_t, 8> old_value;
std::copy(dest, dest + 8, old_value.data());
std::array<uint16_t, 8> new_value;
for (size_t i = 0; i < 8; i++) {
wxString number = value.substr(i * 5, 4);
@ -477,12 +501,8 @@ bool Option::SetGbPalette(const wxString& value) {
new_value[i] = temp;
}
}
std::copy(new_value.begin(), new_value.end(), dest);
if (old_value != new_value) {
CallObservers();
}
return true;
return SetGbPalette(new_value);
}
double Option::GetDoubleMin() const {

View File

@ -180,6 +180,7 @@ public:
Interframe GetInterframe() const;
RenderMethod GetRenderMethod() const;
wxString GetEnumString() const;
std::array<uint16_t, 8> GetGbPalette() const;
wxString GetGbPaletteString() const;
// Sets the value. Will assert on type mismatch.
@ -194,7 +195,8 @@ public:
bool SetInterframe(const Interframe& value);
bool SetRenderMethod(const RenderMethod& value);
bool SetEnumString(const wxString& value);
bool SetGbPalette(const wxString& value);
bool SetGbPalette(const std::array<uint16_t, 8>& value);
bool SetGbPaletteString(const wxString& value);
// Min/Max accessors.
double GetDoubleMin() const;

View File

@ -16,7 +16,7 @@
#include "config/option-id.h"
#include "config/option-proxy.h"
#include "config/option.h"
#include "keep-on-top-styler.h"
#include "dialogs/validated-child.h"
#include "rpi.h"
#include "wayland.h"
#include "widgets/option-validator.h"
@ -218,20 +218,6 @@ private:
}
};
// Helper functions to assert on the returned value.
wxWindow* GetValidatedChild(const wxWindow* parent, const wxString& name) {
wxWindow* window = parent->FindWindow(name);
assert(window);
return window;
}
template <class T>
T* GetValidatedChild(const wxWindow* parent, const wxString& name) {
T* child = wxDynamicCast(GetValidatedChild(parent, name), T);
assert(child);
return child;
}
} // namespace
// static

View File

@ -0,0 +1,365 @@
#include "dialogs/game-boy-config.h"
#include <array>
#include <cstddef>
#include <cstdint>
#include <wx/checkbox.h>
#include <wx/choice.h>
#include <wx/clrpicker.h>
#include <wx/filepicker.h>
#include <wx/panel.h>
#include <wx/stattext.h>
#include <wx/xrc/xmlres.h>
#include "config/option-observer.h"
#include "config/option-proxy.h"
#include "dialogs/validated-child.h"
#include "widgets/group-check-box.h"
#include "widgets/option-validator.h"
#include "wx/object.h"
namespace dialogs {
namespace {
static constexpr size_t kNbPalettes = 3;
// These are the choices for canned colors; their order must match the names in
// wxChoice control.
// clang-format off
static constexpr std::array<std::array<uint16_t, 8>, 9> kDefaultPalettes = {{
// Standard
{0x7FFF, 0x56B5, 0x318C, 0x0000, 0x7FFF, 0x56B5, 0x318C, 0x0000},
// Blue Sea
{0x6200, 0x7E10, 0x7C10, 0x5000, 0x6200, 0x7E10, 0x7C10, 0x5000},
// Dark Night
{0x4008, 0x4000, 0x2000, 0x2008, 0x4008, 0x4000, 0x2000, 0x2008},
// Green Forest
{0x43F0, 0x03E0, 0x4200, 0x2200, 0x43F0, 0x03E0, 0x4200, 0x2200},
// Hot Desert
{0x43FF, 0x03FF, 0x221F, 0x021F, 0x43FF, 0x03FF, 0x221F, 0x021F},
// Pink Dreams
{0x621F, 0x7E1F, 0x7C1F, 0x2010, 0x621F, 0x7E1F, 0x7C1F, 0x2010},
// Weird Colors
{0x621F, 0x401F, 0x001F, 0x2010, 0x621F, 0x401F, 0x001F, 0x2010},
// Real GB Colors
{0x1B8E, 0x02C0, 0x0DA0, 0x1140, 0x1B8E, 0x02C0, 0x0DA0, 0x1140},
// Real 'GB on GBASP' Colors
// TODO: Figure out what the commented out values mean.
{0x7BDE, /*0x23F0*/ 0x5778, /*0x5DC0*/ 0x5640, 0x0000, 0x7BDE, /*0x3678*/ 0x529C, /*0x0980*/ 0x2990, 0x0000},
}};
// clang-format on
// This is a custom wxClientData held by wxPanels implementing the
// GBColorPrefPanel panel. This takes care of setting the wxPanel with the
// appropriate event handlers and validators for its internal widgets.
class GBPalettePanelData final : public wxClientData {
public:
GBPalettePanelData(wxPanel* panel, size_t palette_id);
~GBPalettePanelData() final = default;
private:
// Update `colour_pickers_` to match the `palette_` value and reset
// `default_selector_` if the current palette corresponds to one of the
// default palettes.
void UpdateColourPickers();
// Callback for the `colour_pickers_` wxEVT_COLOURPICKER_CHANGED events.
void OnColourChanged(size_t colour_index, wxColourPickerEvent& event);
// Callback for the `default_selector_` wxEVT_CHOICE events.
void OnDefaultPaletteSelected(wxCommandEvent& event);
// Callback for the "Reset" wxButton event.
void OnPaletteReset(wxCommandEvent& event);
wxChoice* const default_selector_;
const config::OptionID option_id_;
std::array<wxColourPickerCtrl*, 8> colour_pickers_;
std::array<uint16_t, 8> palette_;
friend class PaletteValidator;
};
// Custom validator for kGbPalette options. The "work" palette is held by the
// GBPalettePanelData object attached to this wxPanel.
class PaletteValidator final : public widgets::OptionValidator {
public:
PaletteValidator(GBPalettePanelData* palette_data)
: widgets::OptionValidator(palette_data->option_id_),
palette_data_(palette_data) {
assert(option()->is_gb_palette());
assert(palette_data);
}
~PaletteValidator() final = default;
private:
// widgets::OptionValidator implementation.
wxObject* Clone() const final {
return new PaletteValidator(palette_data_);
}
bool IsWindowValueValid() final { return true; }
bool WriteToWindow() final {
palette_data_->palette_ = option()->GetGbPalette();
palette_data_->UpdateColourPickers();
return true;
}
bool WriteToOption() final {
return option()->SetGbPalette(palette_data_->palette_);
}
GBPalettePanelData* const palette_data_;
};
// Custom validator for a kString Option and a wxFilePickerCtrl widget. This
// also updates "label" to display the file currently saved as the BIOS.
class BIOSPickerValidator final : public widgets::OptionValidator {
public:
BIOSPickerValidator(config::OptionID option_id, wxStaticText* label)
: widgets::OptionValidator(option_id), label_(label) {
assert(label_);
assert(option()->is_string());
}
~BIOSPickerValidator() final = default;
private:
// widgets::OptionValidator implementation.
wxObject* Clone() const final {
return new BIOSPickerValidator(option()->id(), label_);
}
bool IsWindowValueValid() final { return true; }
bool WriteToWindow() final {
const wxString& selection = option()->GetString();
if (selection.empty()) {
label_->SetLabel(_("(None)"));
} else {
wxFilePickerCtrl* file_picker =
wxDynamicCast(GetWindow(), wxFilePickerCtrl);
assert(file_picker);
file_picker->SetPath(selection);
label_->SetLabel(selection);
}
return true;
}
bool WriteToOption() final {
const wxFilePickerCtrl* file_picker =
wxDynamicCast(GetWindow(), wxFilePickerCtrl);
assert(file_picker);
return option()->SetString(file_picker->GetPath());
}
wxStaticText* label_;
};
// Custom validator for the kPrefBorderOn and kPrefBorderAutomatic Options.
// These Options are controlled by a single wxChoice widget and are only ever
// updated by this dialog so we don't need to observe them.
class BorderSelectorValidator final : public wxValidator {
public:
BorderSelectorValidator() = default;
~BorderSelectorValidator() final = default;
private:
// wxValidator implementation.
wxObject* Clone() const final { return new BorderSelectorValidator(); }
bool TransferFromWindow() final {
const wxChoice* borders_selector = wxDynamicCast(GetWindow(), wxChoice);
assert(borders_selector);
switch (borders_selector->GetSelection()) {
case 0:
OPTION(kPrefBorderOn) = false;
OPTION(kPrefBorderAutomatic) = false;
break;
case 1:
OPTION(kPrefBorderOn) = true;
break;
case 2:
OPTION(kPrefBorderOn) = false;
OPTION(kPrefBorderAutomatic) = true;
break;
}
return true;
}
bool Validate(wxWindow*) final { return true; }
bool TransferToWindow() final {
wxChoice* borders_selector = wxDynamicCast(GetWindow(), wxChoice);
assert(borders_selector);
if (!OPTION(kPrefBorderOn) && !OPTION(kPrefBorderAutomatic)) {
borders_selector->SetSelection(0);
} else if (OPTION(kPrefBorderOn)) {
borders_selector->SetSelection(1);
} else {
borders_selector->SetSelection(2);
}
return true;
}
#if WX_HAS_VALIDATOR_SET_WINDOW_OVERRIDE
void SetWindow(wxWindow* window) final {
wxValidator::SetWindow(window);
TransferToWindow();
};
#endif
};
GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id)
: wxClientData(),
default_selector_(GetValidatedChild<wxChoice>(panel, "DefaultPalette")),
option_id_(static_cast<config::OptionID>(
static_cast<size_t>(config::OptionID::kGBPalette0) + palette_id)) {
assert(panel);
assert(palette_id < kNbPalettes);
default_selector_->Bind(
wxEVT_CHOICE, &GBPalettePanelData::OnDefaultPaletteSelected, this);
GetValidatedChild<wxCheckBox>(panel, "UsePalette")
->SetValidator(widgets::OptionSelectedValidator(
config::OptionID::kPrefGBPaletteOption, palette_id));
for (size_t i = 0; i < colour_pickers_.size(); i++) {
wxColourPickerCtrl* colour_picker =
GetValidatedChild<wxColourPickerCtrl>(
panel, wxString::Format("Color%zu", i));
colour_pickers_[i] = colour_picker;
// Update the internal palette reference on colour change.
colour_picker->Bind(wxEVT_COLOURPICKER_CHANGED,
std::bind(&GBPalettePanelData::OnColourChanged,
this, i, std::placeholders::_1),
colour_picker->GetId());
}
GetValidatedChild(panel, "Reset")
->Bind(wxEVT_BUTTON, &GBPalettePanelData::OnPaletteReset, this);
}
void GBPalettePanelData::UpdateColourPickers() {
// Update all of the wxColourPickers based on the current palette values.
for (size_t i = 0; i < palette_.size(); i++) {
const uint16_t element = palette_[i];
colour_pickers_[i]->SetColour(wxColour(((element << 3) & 0xf8),
((element >> 2) & 0xf8),
((element >> 7) & 0xf8)));
}
// See if the current palette corresponds to a default palette.
for (size_t i = 0; i < kDefaultPalettes.size(); i++) {
if (palette_ == kDefaultPalettes[i]) {
default_selector_->SetSelection(i + 1);
return;
}
}
// The configuration is not a default palette, set it to "Custom".
default_selector_->SetSelection(0);
}
void GBPalettePanelData::OnColourChanged(size_t colour_index,
wxColourPickerEvent& event) {
assert(colour_index < palette_.size());
// Update the colour value.
const wxColour colour = event.GetColour();
palette_[colour_index] = ((colour.Red() & 0xf8) >> 3) +
((colour.Green() & 0xf8) << 2) +
((colour.Blue() & 0xf8) << 7);
// Reflect changes to the user.
UpdateColourPickers();
// Let the event propagate.
event.Skip();
}
void GBPalettePanelData::OnDefaultPaletteSelected(wxCommandEvent& event) {
if (event.GetSelection() > 0) {
// Update the palette to one of the default palettes.
palette_ = kDefaultPalettes[event.GetSelection() - 1];
UpdateColourPickers();
}
// Let the event propagate.
event.Skip();
}
void GBPalettePanelData::OnPaletteReset(wxCommandEvent& event) {
// Reset the palette to the last user-saved value.
palette_ = config::Option::ByID(option_id_)->GetGbPalette();
UpdateColourPickers();
// Let the event propagate.
event.Skip();
}
} // namespace
// static
GameBoyConfig* GameBoyConfig::NewInstance(wxWindow* parent) {
assert(parent);
return new GameBoyConfig(parent);
}
GameBoyConfig::GameBoyConfig(wxWindow* parent)
: wxDialog(), keep_on_top_styler_(this) {
#if !wxCHECK_VERSION(3, 1, 0)
// This needs to be set before loading any element on the window. This also
// has no effect since wx 3.1.0, where it became the default.
this->SetExtraStyle(wxWS_EX_VALIDATE_RECURSIVELY);
#endif
wxXmlResource::Get()->LoadDialog(this, parent, "GameBoyConfig");
// System and Peripherals.
GetValidatedChild(this, "System")
->SetValidator(widgets::OptionChoiceValidator(
config::OptionID::kPrefEmulatorType));
// "Display borders" corresponds to 2 variables.
GetValidatedChild(this, "Borders")->SetValidator(BorderSelectorValidator());
// GB BIOS ROM
GetValidatedChild(this, "GBBiosPicker")
->SetValidator(BIOSPickerValidator(
config::OptionID::kGBBiosFile,
GetValidatedChild<wxStaticText>(this, "GBBiosLabel")));
// GBC BIOS ROM
GetValidatedChild(this, "GBCBiosPicker")
->SetValidator(BIOSPickerValidator(
config::OptionID::kGBGBCBiosFile,
GetValidatedChild<wxStaticText>(this, "GBCBiosLabel")));
for (size_t i = 0; i < kNbPalettes; i++) {
// All of the wxPanel logic is handled in its client object.
wxPanel* panel =
GetValidatedChild<wxPanel>(this, wxString::Format("cp%zu", i));
GBPalettePanelData* palette_data = new GBPalettePanelData(panel, i);
// `panel` takes ownership of `palette_data` here.
panel->SetClientObject(palette_data);
panel->SetValidator(PaletteValidator(palette_data));
}
this->Fit();
}
} // namespace dialogs

View File

@ -0,0 +1,30 @@
#ifndef VBAM_WX_DIALOGS_GAME_BOY_CONFIG_H_
#define VBAM_WX_DIALOGS_GAME_BOY_CONFIG_H_
#include <cstddef>
#include <wx/clrpicker.h>
#include <wx/dialog.h>
#include "widgets/keep-on-top-styler.h"
namespace dialogs {
// Manages the Game Boy configuration dialog.
class GameBoyConfig : public wxDialog {
public:
static GameBoyConfig* NewInstance(wxWindow* parent);
~GameBoyConfig() override = default;
private:
// The constructor is private so initialization has to be done via the
// static method. This is because this class is destroyed when its
// owner, `parent` is destroyed. This prevents accidental deletion.
GameBoyConfig(wxWindow* parent);
const widgets::KeepOnTopStyler keep_on_top_styler_;
};
} // namespace dialogs
#endif // VBAM_WX_DIALOGS_GAME_BOY_CONFIG_H_

View File

@ -0,0 +1,28 @@
#ifndef VBAM_WX_DIALOGS_VALIDATED_CHILD_H_
#define VBAM_WX_DIALOGS_VALIDATED_CHILD_H_
#include <cassert>
#include <wx/string.h>
#include <wx/window.h>
namespace dialogs {
// Helper functions to assert on the returned value.
inline wxWindow* GetValidatedChild(const wxWindow* parent,
const wxString& name) {
wxWindow* window = parent->FindWindow(name);
assert(window);
return window;
}
template <class T>
T* GetValidatedChild(const wxWindow* parent, const wxString& name) {
T* child = wxDynamicCast(GetValidatedChild(parent, name), T);
assert(child);
return child;
}
} // namespace dialogs
#endif // VBAM_WX_DIALOGS_VALIDATED_CHILD_H_

View File

@ -29,6 +29,7 @@
#include "config/option.h"
#include "config/user-input.h"
#include "dialogs/display-config.h"
#include "dialogs/game-boy-config.h"
#include "opts.h"
#include "widgets/option-validator.h"
@ -1399,83 +1400,6 @@ wxString CheatListCtrl::OnGetItemText(long item, long column) const
return s;
}
// these are the choices for canned colors; their order must match the
// names in the choice control
static const uint16_t defaultPalettes[][8] = {
{
// Standard
0x7FFF, 0x56B5, 0x318C, 0x0000, 0x7FFF, 0x56B5, 0x318C, 0x0000,
},
{
// Blue Sea
0x6200, 0x7E10, 0x7C10, 0x5000, 0x6200, 0x7E10, 0x7C10, 0x5000,
},
{
// Dark Night
0x4008, 0x4000, 0x2000, 0x2008, 0x4008, 0x4000, 0x2000, 0x2008,
},
{
// Green Forest
0x43F0, 0x03E0, 0x4200, 0x2200, 0x43F0, 0x03E0, 0x4200, 0x2200,
},
{
// Hot Desert
0x43FF, 0x03FF, 0x221F, 0x021F, 0x43FF, 0x03FF, 0x221F, 0x021F,
},
{
// Pink Dreams
0x621F, 0x7E1F, 0x7C1F, 0x2010, 0x621F, 0x7E1F, 0x7C1F, 0x2010,
},
{
// Weird Colors
0x621F, 0x401F, 0x001F, 0x2010, 0x621F, 0x401F, 0x001F, 0x2010,
},
{
// Real GB Colors
0x1B8E, 0x02C0, 0x0DA0, 0x1140, 0x1B8E, 0x02C0, 0x0DA0, 0x1140,
},
{
// Real 'GB on GBASP' Colors
0x7BDE, /*0x23F0*/ 0x5778, /*0x5DC0*/ 0x5640, 0x0000, 0x7BDE, /*0x3678*/ 0x529C, /*0x0980*/ 0x2990, 0x0000,
}
};
// manage the GB color prefs' canned color selecter
static class GBColorConfig_t : public wxEvtHandler {
public:
wxWindow* p;
wxChoice* c;
wxColourPickerCtrl* cp[8];
int pno;
void ColorSel(wxCommandEvent& ev)
{
if (ev.GetSelection() > 0) {
const uint16_t* color = defaultPalettes[ev.GetSelection() - 1];
for (int i = 0; i < 8; i++, color++)
cp[i]->SetColour(wxColor(((*color << 3) & 0xf8),
((*color >> 2) & 0xf8),
((*color >> 7) & 0xf8)));
}
}
void ColorReset(wxCommandEvent& ev)
{
(void)ev; // unused params
const uint16_t* color = &systemGbPalette[pno * 8];
for (int i = 0; i < 8; i++, color++)
cp[i]->SetColour(wxColor(((*color << 3) & 0xf8),
((*color >> 2) & 0xf8),
((*color >> 7) & 0xf8)));
}
void ColorButton(wxCommandEvent& ev)
{
(void)ev; // unused params
c->SetSelection(0);
}
} GBColorConfigHandler[3];
// disable controls if a GBA game is not loaded
class GBACtrlEnabler : public wxValidator {
public:
@ -1701,85 +1625,6 @@ public:
}
} JoyPadConfigHandler[4];
// manage fullscreen mode widget
// technically, it's more than a validator: it modifies the widget as well
class ScreenModeList : public wxValidator {
public:
ScreenModeList()
: wxValidator()
{
}
ScreenModeList(const ScreenModeList& e)
: wxValidator()
{
(void)e; // unused params
}
wxObject* Clone() const { return new ScreenModeList(*this); }
bool Validate(wxWindow* p) {
(void)p; // unused params
return true;
}
bool TransferToWindow()
{
wxChoice* c = wxStaticCast(GetWindow(), wxChoice);
wxDisplay d(wxDisplay::GetFromWindow(c->GetParent()));
c->Clear();
int modeno = 0, bestmode = 0;
int bm_bpp = 0;
c->Append(_("Desktop mode"));
// probably ought to just disable this whole control on UNIX/X11 since
// wxDisplay is so broken.
vm = d.GetModes();
wxString s;
for (size_t i = 0; i < vm.size(); i++) {
s.Printf(_("%d x %d - %d bpp @ %d Hz"), vm[i].w, vm[i].h, vm[i].bpp, vm[i].refresh);
c->Append(s);
if (!modeno && gopts.fs_mode.w == vm[i].w && gopts.fs_mode.h == vm[i].h) {
if (gopts.fs_mode.bpp == vm[i].bpp && gopts.fs_mode.refresh == vm[i].refresh)
modeno = i + 1;
else if (vm[i].bpp == gopts.fs_mode.bpp && bm_bpp != gopts.fs_mode.bpp) {
bestmode = i + 1;
bm_bpp = vm[i].bpp;
} else if (bm_bpp != gopts.fs_mode.bpp && bm_bpp != 32 && vm[i].bpp == 32) {
bm_bpp = vm[i].bpp;
bestmode = i + 1;
} else if (bm_bpp != gopts.fs_mode.bpp && bm_bpp < 24 && vm[i].bpp == 24) {
bm_bpp = vm[i].bpp;
bestmode = i + 1;
} else if (bm_bpp != gopts.fs_mode.bpp && bm_bpp < 24 && bm_bpp != 16 && vm[i].bpp == 16) {
bm_bpp = vm[i].bpp;
bestmode = i + 1;
} else if (!bm_bpp) {
bm_bpp = vm[i].bpp;
bestmode = i + 1;
}
}
}
if (!modeno && bestmode)
modeno = bestmode;
c->SetSelection(modeno);
return true;
}
bool TransferFromWindow()
{
int bestmode = wxStaticCast(GetWindow(), wxChoice)->GetSelection();
if (!bestmode)
gopts.fs_mode.h = gopts.fs_mode.w = gopts.fs_mode.bpp = gopts.fs_mode.refresh = 0;
else
gopts.fs_mode = vm[bestmode - 1];
return true;
}
private:
wxArrayVideoModes vm;
};
// this is the cmd table index for the accel tree ctrl
// one of the "benefits" of using TreeItemData is that we have to
// malloc them all, because treectrl destructor will free them all
@ -2993,16 +2838,6 @@ bool MainFrame::BindControls()
rb = SafeXRCCTRL<wxRadioButton>(d, n); \
rb->SetValidator(wxBoolIntValidator(&o, v)); \
} while (0)
#define getrbb(n, o) \
do { \
rb = SafeXRCCTRL<wxRadioButton>(d, n); \
rb->SetValidator(wxGenericValidator(&o)); \
} while (0)
#define getrbbr(n, o) \
do { \
rb = SafeXRCCTRL<wxRadioButton>(d, n); \
rb->SetValidator(wxBoolRevValidator(&o)); \
} while (0)
wxBoolEnValidator* benval;
wxBoolEnHandler* ben;
#define getbe(n, o, cv, t, wt) \
@ -3290,11 +3125,6 @@ bool MainFrame::BindControls()
cb = SafeXRCCTRL<wxCheckBox>(d, n); \
cb->SetValidator(wxGenericValidator(&o)); \
} while (0)
#define getcbi(n, o) \
do { \
cb = SafeXRCCTRL<wxCheckBox>(d, n); \
cb->SetValidator(wxBoolIntValidator(&o, 1)); \
} while (0)
wxSpinCtrl* sc;
#define getsc(n, o) \
do { \
@ -3357,106 +3187,14 @@ bool MainFrame::BindControls()
}
d = LoadXRCDialog("UIConfig");
{
getcbb("HideMenuBar", gopts.hide_menu_bar);
}
#define getcbbe(n, o) getbe(n, o, cb, wxCheckBox, CB)
wxBoolIntEnValidator* bienval;
(void)bienval; // not used yet
#define getbie(n, o, v, cv, t, wt) \
do { \
cv = SafeXRCCTRL<t>(d, n); \
cv->SetValidator(wxBoolIntEnValidator(&o, v, v)); \
bienval = wxStaticCast(cv->GetValidator(), wxBoolIntEnValidator); \
static wxBoolEnHandler _ben; \
ben = &_ben; \
wx##wt##BoolEnHandlerConnect(cv, wxID_ANY, _ben); \
} while (0)
#define addbie(n) \
do { \
ben->controls.push_back(n); \
bienval->controls.push_back(n); \
} while (0)
#define addbier(n, r) \
do { \
ben->controls.push_back(n); \
ben->reverse.push_back(r); \
bienval->controls.push_back(n); \
bienval->reverse.push_back(r); \
} while (0)
#define getcbie(n, o, v) getbie(n, o, v, cb, wxCheckBox, CB)
{ getcbb("HideMenuBar", gopts.hide_menu_bar); }
wxFilePickerCtrl* fp;
#define getfp(n, o, l) \
do { \
fp = SafeXRCCTRL<wxFilePickerCtrl>(d, n); \
fp->SetValidator(wxFileDirPickerValidator(&o, l)); \
} while (0)
d = LoadXRCropertySheetDialog("GameBoyConfig");
{
/// System and Peripherals
ch = GetValidatedChild<wxChoice, wxGenericValidator>(d, "System", wxGenericValidator(&gbEmulatorType));
// "Display borders" corresponds to 2 variables, so it is handled
// in command handler. Plus making changes might require resizing
// game area. Validation only here.
SafeXRCCTRL<wxChoice>(d, "Borders");
/// GB Boot ROM
wxStaticText *label = SafeXRCCTRL<wxStaticText>(d, "BiosFile");
if (!gopts.gb_bios.empty()) label->SetLabel(gopts.gb_bios);
getfp("BootRom", gopts.gb_bios, label);
getlab("BootRomLab");
/// GBC
wxStaticText *clabel = SafeXRCCTRL<wxStaticText>(d, "CBiosFile");
if (!gopts.gbc_bios.empty()) clabel->SetLabel(gopts.gbc_bios);
getfp("CBootRom", gopts.gbc_bios, clabel);
getlab("CBootRomLab");
/// Custom Colors
//getcbi("Color", gbColorOption);
wxFarRadio* r = NULL;
for (int i = 0; i < 3; i++) {
wxString pn;
// NOTE: wx2.9.1 behaves differently for referenced nodes
// than 2.8! Unless there is an actual child node, the ID field
// will not be overwritten. This means that there should be a
// dummy child node (e.g. position=(0,0)). If you get
// "Unable to load dialog GameBoyConfig from resources", this is
// probably the reason.
pn.Printf(wxT("cp%d"), i + 1);
wxWindow* w = SafeXRCCTRL<wxWindow>(d, pn);
GBColorConfigHandler[i].p = w;
GBColorConfigHandler[i].pno = i;
wxFarRadio* cb = SafeXRCCTRL<wxFarRadio>(w, "UsePalette");
if (r)
cb->SetGroup(r);
else
r = cb;
cb->SetValidator(wxBoolIntValidator(&gbPaletteOption, i));
ch = SafeXRCCTRL<wxChoice>(w, "ColorSet");
GBColorConfigHandler[i].c = ch;
for (int j = 0; j < 8; j++) {
wxString s;
s.Printf(wxT("Color%d"), j);
wxColourPickerCtrl* cp = SafeXRCCTRL<wxColourPickerCtrl>(w, s);
GBColorConfigHandler[i].cp[j] = cp;
cp->SetValidator(wxColorValidator(&systemGbPalette[i * 8 + j]));
}
w->Connect(wxEVT_COMMAND_CHOICE_SELECTED,
wxCommandEventHandler(GBColorConfig_t::ColorSel),
NULL, &GBColorConfigHandler[i]);
w->Connect(XRCID("Reset"), wxEVT_COMMAND_BUTTON_CLICKED,
wxCommandEventHandler(GBColorConfig_t::ColorReset),
NULL, &GBColorConfigHandler[i]);
w->Connect(wxID_ANY, wxEVT_COMMAND_COLOURPICKER_CHANGED,
wxCommandEventHandler(GBColorConfig_t::ColorButton),
NULL, &GBColorConfigHandler[i]);
}
d->Fit();
}
dialogs::GameBoyConfig::NewInstance(this);
d = LoadXRCropertySheetDialog("GameBoyAdvanceConfig");
{
/// System and peripherals
@ -3594,7 +3332,6 @@ bool MainFrame::BindControls()
d->Fit();
}
wxDialog* joyDialog = LoadXRCropertySheetDialog("JoypadConfig");
wxFarRadio* r = 0;
for (int i = 0; i < 4; i++) {
wxString pn;
@ -3606,15 +3343,9 @@ bool MainFrame::BindControls()
// probably the reason.
pn.Printf(wxT("joy%d"), i + 1);
wxWindow* w = SafeXRCCTRL<wxWindow>(joyDialog, pn);
wxFarRadio* cb;
cb = SafeXRCCTRL<wxFarRadio>(w, "DefaultConfig");
if (r)
cb->SetGroup(r);
else
r = cb;
cb->SetValidator(wxBoolIntValidator(&gopts.default_stick, i + 1));
w->FindWindow("DefaultConfig")
->SetValidator(wxBoolIntValidator(&gopts.default_stick, i + 1));
wxWindow *prev = NULL, *prevp = NULL;
for (const config::GameKey& game_key : config::kAllGameKeys) {

View File

@ -529,7 +529,7 @@ void load_opts(bool first_time_launch) {
case config::Option::Type::kGbPalette: {
wxString temp;
cfg->Read(opt.config_name(), &temp, opt.GetGbPaletteString());
opt.SetGbPalette(temp);
opt.SetGbPaletteString(temp);
break;
}
}
@ -768,7 +768,7 @@ void opt_set(const wxString& name, const wxString& val) {
opt->SetEnumString(val);
return;
case config::Option::Type::kGbPalette:
opt->SetGbPalette(val);
opt->SetGbPaletteString(val);
return;
}
}

View File

@ -26,10 +26,7 @@ extern struct opts_t {
wxVideoMode fs_mode;
/// GB
wxString gb_bios;
bool gb_colorizer_hack = false;
bool gb_lcd_filter = false;
wxString gbc_bios;
bool print_auto_page = true;
bool print_screen_cap = false;
wxString gb_rom_dir;

View File

@ -115,7 +115,15 @@ GameArea::GameArea()
config::OptionID::kDispStretch},
std::bind(&GameArea::ResetPanel, this)),
scale_observer_(config::OptionID::kDispScale,
std::bind(&GameArea::AdjustSize, this, true)) {
std::bind(&GameArea::AdjustSize, this, true)),
gb_border_observer_(
config::OptionID::kPrefBorderOn,
std::bind(&GameArea::OnGBBorderChanged, this, std::placeholders::_1)),
gb_palette_observer_(
{config::OptionID::kGBPalette0, config::OptionID::kGBPalette1,
config::OptionID::kGBPalette2,
config::OptionID::kPrefGBPaletteOption},
std::bind(&gbResetPalette)) {
SetSizer(new wxBoxSizer(wxVERTICAL));
// all renderers prefer 32-bit
// well, "simple" prefers 24-bit, but that's not available for filters
@ -269,23 +277,21 @@ void GameArea::LoadGame(const wxString& name)
// Set up the core for the colorizer hack.
setColorizerHack(OPTION(kGBColorizerHack));
bool use_bios =
const bool use_bios =
gbCgbMode ? gopts.use_bios_file_gbc : gopts.use_bios_file_gb;
wxCharBuffer fnb(UTF8((gbCgbMode ? gopts.gbc_bios : gopts.gb_bios)));
const char* fn = fnb.data();
gbCPUInit(fn, use_bios);
const wxString bios_file = gbCgbMode ? OPTION(kGBGBCBiosFile).Get() : OPTION(kGBBiosFile).Get();
gbCPUInit(bios_file.To8BitData().data(), use_bios);
if (use_bios && !coreOptions.useBios) {
wxLogError(_("Could not load BIOS %s"), (gbCgbMode ? gopts.gbc_bios : gopts.gb_bios).mb_str());
wxLogError(_("Could not load BIOS %s"), bios_file);
// could clear use flag & file name now, but better to force
// user to do it
}
gbReset();
if (gbBorderOn) {
if (OPTION(kPrefBorderOn)) {
basic_width = gbBorderLineSkip = SGBWidth;
basic_height = SGBHeight;
gbBorderColumnSkip = (SGBWidth - GBWidth) / 2;
@ -2629,3 +2635,14 @@ void GameArea::ShowMenuBar()
menu_bar_hidden = false;
#endif
}
void GameArea::OnGBBorderChanged(config::Option* option) {
if (game_type() == IMAGE_GB && gbSgbMode) {
if (option->GetBool()) {
AddBorder();
gbSgbRenderBorder();
} else {
DelBorder();
}
}
}

View File

@ -0,0 +1,194 @@
#include "widgets/group-check-box.h"
namespace widgets {
namespace {
wxWindow* FindTopLevelWindow(wxWindow* window) {
while (window != nullptr && !window->IsTopLevel()) {
window = window->GetParent();
}
assert(window);
return window;
}
GroupCheckBox* FindGroupCheckBox(wxWindow* window,
const wxString& name,
GroupCheckBox* current) {
if (window == current) {
return nullptr;
}
if (window->IsKindOf(wxCLASSINFO(GroupCheckBox))) {
GroupCheckBox* check_box = wxDynamicCast(window, GroupCheckBox);
if (check_box->GetName() == name) {
return check_box;
}
}
for (wxWindow* child : window->GetChildren()) {
GroupCheckBox* check_box = FindGroupCheckBox(child, name, current);
if (check_box) {
return check_box;
}
}
return nullptr;
}
} // namespace
extern const char GroupCheckBoxNameStr[] = "groupcheck";
GroupCheckBox::GroupCheckBox() : wxCheckBox(), next_(this) {}
GroupCheckBox::GroupCheckBox(wxWindow* parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name)
: next_(this) {
Create(parent, id, label, pos, size, style, validator, name);
}
GroupCheckBox::~GroupCheckBox() {
RemoveFromGroup();
}
bool GroupCheckBox::Create(wxWindow* parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos,
const wxSize& size,
long style,
const wxValidator& validator,
const wxString& name) {
if (!wxCheckBox::Create(parent, id, label, pos, size, style, validator,
name)) {
return false;
}
AddToGroup();
initialized_ = true;
return true;
}
void GroupCheckBox::AddToGroup() {
assert(next_ == this);
if (GetName().IsEmpty()) {
// No name means a singleton.
return;
}
// Find another GroupCheckBox with the same name as this.
GroupCheckBox* other_box =
FindGroupCheckBox(FindTopLevelWindow(this), GetName(), this);
if (!other_box) {
return;
}
// Find the previous GroupCheckBox to put this in the circular linked list.
GroupCheckBox* prev_box = other_box;
while (prev_box->next_ != other_box) {
prev_box = prev_box->next_;
}
next_ = other_box;
prev_box->next_ = this;
}
void GroupCheckBox::RemoveFromGroup() {
GroupCheckBox* prev_box = this;
while (prev_box->next_ != this) {
prev_box = prev_box->next_;
}
// Update the link.
prev_box->next_ = next_;
next_ = this;
}
void GroupCheckBox::OnCheck(wxCommandEvent& event) {
UpdateGroup();
// Let the event propagate.
event.Skip();
}
void GroupCheckBox::UpdateGroup() {
if (next_ == this) {
// Nothing more to do if not part of a group.
return;
}
if (GetValue()) {
for (GroupCheckBox* check_box = next_; check_box != this;
check_box = check_box->next_) {
check_box->SetValue(false);
}
} else {
// Find a checked GroupCheckBox.
GroupCheckBox* check_box = next_;
while (check_box != this) {
if (check_box->GetValue()) {
break;
}
check_box = check_box->next_;
}
// Ensure at least one GroupCheckBox is checked.
if (check_box == this) {
SetValue(true);
}
}
}
void GroupCheckBox::SetValue(bool val) {
wxCheckBox::SetValue(val);
if (initialized_) {
UpdateGroup();
}
}
void GroupCheckBox::SetName(const wxString& name) {
wxCheckBox::SetName(name);
if (initialized_) {
RemoveFromGroup();
AddToGroup();
}
}
wxIMPLEMENT_DYNAMIC_CLASS(GroupCheckBox, wxCheckBox);
// clang-format off
wxBEGIN_EVENT_TABLE(GroupCheckBox, wxCheckBox)
EVT_CHECKBOX(wxID_ANY, GroupCheckBox::OnCheck)
wxEND_EVENT_TABLE();
// clang-format on
GroupCheckBoxXmlHandler::GroupCheckBoxXmlHandler() : wxXmlResourceHandler() {
AddWindowStyles();
}
wxObject* GroupCheckBoxXmlHandler::DoCreateResource() {
XRC_MAKE_INSTANCE(control, GroupCheckBox)
control->Create(m_parentAsWindow, GetID(), GetText("label"), GetPosition(),
GetSize(), GetStyle(), wxDefaultValidator, GetName());
SetupWindow(control);
return control;
}
bool GroupCheckBoxXmlHandler::CanHandle(wxXmlNode* node) {
return IsOfClass(node, "GroupCheckBox");
}
wxIMPLEMENT_DYNAMIC_CLASS(GroupCheckBoxXmlHandler, wxXmlResourceHandler);
} // namespace widgets

View File

@ -0,0 +1,77 @@
#ifndef VBAM_WX_WIDGETS_GROUP_CHECK_BOX_H_
#define VBAM_WX_WIDGETS_GROUP_CHECK_BOX_H_
#include <wx/checkbox.h>
#include <wx/string.h>
#include <wx/xrc/xmlres.h>
namespace widgets {
extern const char GroupCheckBoxNameStr[];
// A custom check box that is part of a group where only one element can be
// active at any given time. The other check boxes in the group are identified
// as GroupCheckBoxes with the same name within the same top-level window.
//
// Internally, the other GroupCheckBoxes in the same group are tracked in a
// circular linked list, via the `next_` pointer. On initialization, this links
// to this object. The list is updated when the GroupCheckBox name is set or
// reset.
class GroupCheckBox : public wxCheckBox {
public:
GroupCheckBox();
GroupCheckBox(
wxWindow* parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxString::FromAscii(GroupCheckBoxNameStr));
~GroupCheckBox() override;
bool Create(
wxWindow* parent,
wxWindowID id,
const wxString& label,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = 0,
const wxValidator& validator = wxDefaultValidator,
const wxString& name = wxString::FromAscii(GroupCheckBoxNameStr));
// wxCheckBox implementation.
void SetValue(bool val) override;
void SetName(const wxString& name) override;
wxDECLARE_DYNAMIC_CLASS(GroupCheckBox);
wxDECLARE_EVENT_TABLE();
private:
void AddToGroup();
void RemoveFromGroup();
void OnCheck(wxCommandEvent& event);
void UpdateGroup();
bool initialized_ = false;
GroupCheckBox* next_;
};
// Handler to load the resource from an XRC file as a "GroupCheckBox" object.
class GroupCheckBoxXmlHandler : public wxXmlResourceHandler {
public:
GroupCheckBoxXmlHandler();
~GroupCheckBoxXmlHandler() override = default;
wxObject* DoCreateResource() override;
bool CanHandle(wxXmlNode* node) override;
wxDECLARE_DYNAMIC_CLASS(wxCheckBoxXmlHandler);
};
} // namespace widgets
#endif // VBAM_WX_WIDGETS_GROUP_CHECK_BOX_H_

View File

@ -3,39 +3,11 @@
// utility widgets
#include <cstdint>
#include <vector>
#include <wx/checkbox.h>
#include <wx/stattext.h>
#include <wx/valgen.h>
using std::uint8_t;
using std::uint16_t;
using std::uint32_t;
using std::int8_t;
using std::int16_t;
using std::int32_t;
// simple radio button not under the same parent window
// note that it must be checkbox, as wx radio buttons have rigid behavior
class wxFarRadio : public wxCheckBox {
public:
wxFarRadio();
virtual ~wxFarRadio();
void SetValue(bool val);
// join this group with widget(s) in grp
void SetGroup(class wxFarRadio* grp);
// turn into a singleton
void BreakGroup();
// iterate over members in group (ring)
wxFarRadio* GetNext();
protected:
void UpdatedValue();
void UpdateEvt(wxCommandEvent& ev);
wxFarRadio* Next;
DECLARE_DYNAMIC_CLASS(wxFarRadio)
DECLARE_EVENT_TABLE()
};
// boolean copy-only validator that uses a constant int
// may be attached to radio button or checkbox
class wxBoolIntValidator : public wxValidator {
@ -110,8 +82,6 @@ protected:
bool* vptr;
};
#include <wx/stattext.h>
// wxFilePickerCtrl/wxDirPickerCtrl copy-only vvalidator
class wxFileDirPickerValidator : public wxValidator {
public:
@ -144,56 +114,10 @@ protected:
wxStaticText* vlabel;
};
// color copy-only validator that supports either 32-bit or 16-bit color
// value (but not endianness..)
// FIXME: only supported formats are RGB888 and BGR555
#include <stdint.h>
class wxColorValidator : public wxValidator {
public:
wxColorValidator(uint32_t* vptr)
: wxValidator()
, ptr32(vptr)
, ptr16(0)
{
}
wxColorValidator(uint16_t* vptr)
: wxValidator()
, ptr32(0)
, ptr16(vptr)
{
}
wxColorValidator(const wxColorValidator& v)
: wxValidator()
, ptr32(v.ptr32)
, ptr16(v.ptr16)
{
}
wxObject* Clone() const
{
return new wxColorValidator(*this);
}
bool TransferToWindow();
bool TransferFromWindow();
bool Validate(wxWindow* p)
{
(void)p; // unused params
return true;
}
protected:
uint32_t* ptr32;
uint16_t* ptr16;
};
// Copy-only validators for checkboxes and radio buttons that enables a set
// of dependent widgets
// Requires an event handler during run-time
#include <vector>
#include <wx/valgen.h>
// there's probably a standard wxWindowList or some such, but it's
// undocumented and I prefer arrays
typedef std::vector<wxWindow*> wxWindow_v;

View File

@ -8,108 +8,6 @@
#include <wx/wx.h>
#include <wx/spinctrl.h>
wxFarRadio::wxFarRadio()
: wxCheckBox()
{
Next = this;
}
wxFarRadio::~wxFarRadio()
{
BreakGroup();
}
void wxFarRadio::SetValue(bool val)
{
wxCheckBox::SetValue(val);
UpdatedValue();
}
void wxFarRadio::SetGroup(class wxFarRadio* grp)
{
if (grp == this)
return;
wxFarRadio* checked = GetValue() ? this : NULL;
for (wxFarRadio* gp = Next; gp != this; gp = gp->Next) {
if (gp == grp)
return;
if (gp->GetValue())
checked = gp;
}
wxFarRadio* link = Next;
Next = grp;
bool clear_checked = false;
wxFarRadio* gp;
for (gp = grp; gp->Next != grp; gp = gp->Next) {
if (checked && GetValue())
clear_checked = true;
}
gp->Next = link;
if (checked && gp->GetValue())
clear_checked = true;
if (clear_checked)
checked->SetValue(false);
int l;
for (l = 1, gp = Next; gp != this; gp = gp->Next, l++)
;
}
void wxFarRadio::BreakGroup()
{
wxFarRadio** gp;
for (gp = &Next; *gp != this; gp = &(*gp)->Next)
;
*gp = Next;
Next = this;
}
wxFarRadio* wxFarRadio::GetNext()
{
return Next;
}
void wxFarRadio::UpdatedValue()
{
if (!GetValue()) {
wxFarRadio* gp;
// just like system wx, ensure at least one always checked
for (gp = Next; gp != this; gp = gp->Next)
if (gp->GetValue())
break;
if (gp == this) {
SetValue(true);
return;
}
} else
for (wxFarRadio* gp = Next; gp != this; gp = gp->Next)
gp->SetValue(false);
}
void wxFarRadio::UpdateEvt(wxCommandEvent& ev)
{
UpdatedValue();
ev.Skip();
}
IMPLEMENT_DYNAMIC_CLASS(wxFarRadio, wxCheckBox);
BEGIN_EVENT_TABLE(wxFarRadio, wxCheckBox)
EVT_CHECKBOX(wxID_ANY, wxFarRadio::UpdateEvt)
END_EVENT_TABLE()
bool wxBoolIntValidator::TransferToWindow()
{
if (!vptr)
@ -250,50 +148,6 @@ bool wxFileDirPickerValidator::TransferFromWindow()
return false;
}
#include <wx/clrpicker.h>
bool wxColorValidator::TransferToWindow()
{
if (!ptr32 && !ptr16)
return false;
wxColourPickerCtrl* cp = wxDynamicCast(GetWindow(), wxColourPickerCtrl);
if (!cp)
return false;
if (ptr32)
cp->SetColour(wxColor(((*ptr32 >> 16) & 0xff),
((*ptr32 >> 8) & 0xff),
((*ptr32) & 0xff)));
else
cp->SetColour(wxColor(((*ptr16 << 3) & 0xf8),
((*ptr16 >> 2) & 0xf8),
((*ptr16 >> 7) & 0xf8)));
return true;
}
bool wxColorValidator::TransferFromWindow()
{
if (!ptr32 && !ptr16)
return false;
wxColourPickerCtrl* cp = wxDynamicCast(GetWindow(), wxColourPickerCtrl);
if (!cp)
return false;
wxColor c = cp->GetColour();
if (ptr32)
*ptr32 = ((c.Red() & 0xff) << 16) + ((c.Green() & 0xff) << 8) + ((c.Blue() & 0xff));
else
*ptr16 = ((c.Red() & 0xf8) >> 3) + ((c.Green() & 0xf8) << 2) + ((c.Blue() & 0xf8) << 7);
return true;
}
static void enable(wxWindow_v controls, std::vector<int> reverse, bool en)
{
for (size_t i = 0; i < controls.size(); i++)

View File

@ -39,6 +39,7 @@
#include "config/user-input.h"
#include "strutils.h"
#include "wayland.h"
#include "widgets/group-check-box.h"
namespace {
static const wxString kOldConfigFileName("vbam.conf");
@ -294,6 +295,7 @@ bool wxvbamApp::OnInit() {
// note: if linking statically, next 2 pull in lot of unused code
// maybe in future if not wxSHARED, load only builtin-needed handlers
xr->InitAllHandlers();
xr->AddHandler(new widgets::GroupCheckBoxXmlHandler());
wxInitAllImageHandlers();
get_config_path(config_path);
// first, load override xrcs

View File

@ -605,6 +605,7 @@ public:
void HidePointer();
void HideMenuBar();
void ShowMenuBar();
void OnGBBorderChanged(config::Option* option);
protected:
void MouseEvent(wxMouseEvent&);
@ -621,6 +622,8 @@ protected:
private:
config::OptionsObserver render_observer_;
config::OptionsObserver scale_observer_;
config::OptionsObserver gb_border_observer_;
config::OptionsObserver gb_palette_observer_;
};
// wxString version of OSD message

View File

@ -4,9 +4,9 @@
<object class="wxBoxSizer">
<orient>wxVERTICAL</orient>
<object class="sizeritem">
<object class="wxChoice" name="ColorSet">
<object class="wxChoice" name="DefaultPalette">
<content>
<item/>
<item>Custom</item>
<item>Standard</item>
<item>Blue Sea</item>
<item>Dark Night</item>
@ -80,7 +80,7 @@
<object class="sizeritem">
<object class="wxBoxSizer">
<object class="sizeritem">
<object class="wxCheckBox" name="UsePalette" subclass="wxFarRadio">
<object class="GroupCheckBox" name="UsePalette">
<label>Use this palette</label>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>

View File

@ -87,7 +87,7 @@
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxFilePickerCtrl" name="BootRom">
<object class="wxFilePickerCtrl" name="GBBiosPicker">
<message>Select a File</message>
<wildcard>BIOS files (*.bin;*.rom)|*.bin;*.rom|All files|*</wildcard>
<size>472,30</size>
@ -109,7 +109,7 @@
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxFilePickerCtrl" name="CBootRom">
<object class="wxFilePickerCtrl" name="GBCBiosPicker">
<message>Select a File</message>
<wildcard>BIOS files (*.bin;*.rom)|*.bin;*.rom|All files|*</wildcard>
<size>472,30</size>
@ -134,7 +134,7 @@
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxStaticText" name="BiosFile">
<object class="wxStaticText" name="GBBiosLabel">
<label>(None)</label>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
@ -155,7 +155,7 @@
<border>5</border>
</object>
<object class="sizeritem">
<object class="wxStaticText" name="CBiosFile">
<object class="wxStaticText" name="GBCBiosLabel">
<label>(None)</label>
</object>
<flag>wxALL|wxALIGN_CENTRE_VERTICAL</flag>
@ -174,19 +174,19 @@
<object class="sizeritem">
<object class="wxNotebook">
<object class="notebookpage">
<object_ref name="cp1" ref="GBColorPrefPanel">
<object_ref name="cp0" ref="GBColorPrefPanel">
<pos>0,0</pos>
</object_ref>
<label>Default</label>
</object>
<object class="notebookpage">
<object_ref name="cp2" ref="GBColorPrefPanel">
<object_ref name="cp1" ref="GBColorPrefPanel">
<pos>0,0</pos>
</object_ref>
<label>User 1</label>
</object>
<object class="notebookpage">
<object_ref name="cp3" ref="GBColorPrefPanel">
<object_ref name="cp2" ref="GBColorPrefPanel">
<pos>0,0</pos>
</object_ref>
<label>User 2</label>

View File

@ -511,7 +511,7 @@
<flag>wxEXPAND|wxALL</flag>
</object>
<object class="sizeritem">
<object class="wxCheckBox" name="DefaultConfig" subclass="wxFarRadio">
<object class="GroupCheckBox" name="DefaultConfig">
<label>Use as default</label>
</object>
</object>