[Dialogs] Save and restore dialog positions

This changes most dialogs in the wx frontend to use a common base
abstraction, `dialogs::BaseDialog`. This base class sets up common
style options for every dialog and automatically saves and restores the
dialog position.

In addition, this moves the first time show position of the dialog to be
slightly to the bottom and right of the main window, even if the main
window is on a screen other than the main screen.
This commit is contained in:
Fabrice de Gans 2024-04-08 16:58:04 -07:00 committed by Rafael Kitover
parent 41952d0625
commit 32627f6b85
27 changed files with 352 additions and 303 deletions

View File

@ -31,6 +31,8 @@ set(VBAM_WX_COMMON
config/user-input.h config/user-input.h
dialogs/accel-config.cpp dialogs/accel-config.cpp
dialogs/accel-config.h dialogs/accel-config.h
dialogs/base-dialog.cpp
dialogs/base-dialog.h
dialogs/directories-config.cpp dialogs/directories-config.cpp
dialogs/directories-config.h dialogs/directories-config.h
dialogs/display-config.cpp dialogs/display-config.cpp
@ -45,7 +47,6 @@ set(VBAM_WX_COMMON
dialogs/joypad-config.h dialogs/joypad-config.h
dialogs/sound-config.cpp dialogs/sound-config.cpp
dialogs/sound-config.h dialogs/sound-config.h
dialogs/validated-child.h
drawing.h drawing.h
extra-translations.cpp extra-translations.cpp
gfxviewers.cpp gfxviewers.cpp
@ -79,6 +80,8 @@ set(VBAM_WX_COMMON
widgets/user-input-ctrl.h widgets/user-input-ctrl.h
widgets/sdljoy.cpp widgets/sdljoy.cpp
widgets/sdljoy.h widgets/sdljoy.h
widgets/utils.cpp
widgets/utils.h
widgets/webupdatedef.h widgets/webupdatedef.h
widgets/wxmisc.h widgets/wxmisc.h
widgets/wxmisc.cpp widgets/wxmisc.cpp

View File

@ -1714,7 +1714,7 @@ EVT_HANDLER_MASK(Disassemble, "Disassemble...", CMDEN_GB | CMDEN_GBA)
EVT_HANDLER(Logging, "Logging...") EVT_HANDLER(Logging, "Logging...")
{ {
wxDialog* dlg = wxGetApp().frame->logdlg; wxDialog* dlg = wxGetApp().frame->logdlg.get();
dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER); dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER);
dlg->Show(); dlg->Show();
dlg->Raise(); dlg->Raise();

View File

@ -5,12 +5,10 @@
#include <wx/listbox.h> #include <wx/listbox.h>
#include <wx/menu.h> #include <wx/menu.h>
#include <wx/msgdlg.h> #include <wx/msgdlg.h>
#include <wx/xrc/xmlres.h>
#include "wx/config/shortcuts.h" #include "wx/config/shortcuts.h"
#include "wx/config/user-input.h" #include "wx/config/user-input.h"
#include "wx/dialogs/validated-child.h" #include "wx/dialogs/base-dialog.h"
#include "wx/opts.h"
#include "wx/widgets/user-input-ctrl.h" #include "wx/widgets/user-input-ctrl.h"
#include "wx/wxvbam.h" #include "wx/wxvbam.h"
@ -132,21 +130,16 @@ AccelConfig* AccelConfig::NewInstance(wxWindow* parent, wxMenuBar* menu, wxMenu*
} }
AccelConfig::AccelConfig(wxWindow* parent, wxMenuBar* menu, wxMenu* recents) AccelConfig::AccelConfig(wxWindow* parent, wxMenuBar* menu, wxMenu* recents)
: wxDialog(), keep_on_top_styler_(this) { : BaseDialog(parent, "AccelConfig") {
assert(parent);
assert(menu); assert(menu);
// Load the dialog XML.
const bool success = wxXmlResource::Get()->LoadDialog(this, parent, "AccelConfig");
assert(success);
// Loads the various dialog elements. // Loads the various dialog elements.
tree_ = GetValidatedChild<wxTreeCtrl>(this, "Commands"); tree_ = GetValidatedChild<wxTreeCtrl>("Commands");
current_keys_ = GetValidatedChild<wxListBox>(this, "Current"); current_keys_ = GetValidatedChild<wxListBox>("Current");
assign_button_ = GetValidatedChild(this, "Assign"); assign_button_ = GetValidatedChild("Assign");
remove_button_ = GetValidatedChild(this, "Remove"); remove_button_ = GetValidatedChild("Remove");
key_input_ = GetValidatedChild<widgets::UserInputCtrl>(this, "Shortcut"); key_input_ = GetValidatedChild<widgets::UserInputCtrl>("Shortcut");
currently_assigned_label_ = GetValidatedChild<wxControl>(this, "AlreadyThere"); currently_assigned_label_ = GetValidatedChild<wxControl>("AlreadyThere");
// Configure the key input. // Configure the key input.
key_input_->MoveBeforeInTabOrder(assign_button_); key_input_->MoveBeforeInTabOrder(assign_button_);

View File

@ -3,11 +3,10 @@
#include <unordered_map> #include <unordered_map>
#include <wx/dialog.h>
#include <wx/treectrl.h> #include <wx/treectrl.h>
#include "wx/config/shortcuts.h" #include "wx/config/shortcuts.h"
#include "wx/widgets/keep-on-top-styler.h" #include "wx/dialogs/base-dialog.h"
// Forward declarations. // Forward declarations.
class wxControl; class wxControl;
@ -23,7 +22,7 @@ class UserInputCtrl;
namespace dialogs { namespace dialogs {
// Manages the shortcuts editor dialog. // Manages the shortcuts editor dialog.
class AccelConfig : public wxDialog { class AccelConfig : public BaseDialog {
public: public:
static AccelConfig* NewInstance(wxWindow* parent, wxMenuBar* menu_bar, wxMenu* recents); static AccelConfig* NewInstance(wxWindow* parent, wxMenuBar* menu_bar, wxMenu* recents);
@ -74,8 +73,6 @@ private:
config::Shortcuts config_shortcuts_; config::Shortcuts config_shortcuts_;
int selected_command_ = 0; int selected_command_ = 0;
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -0,0 +1,137 @@
#include "wx/dialogs/base-dialog.h"
#include <cassert>
#include <wx/persist.h>
#include <wx/persist/toplevel.h>
#include <wx/xrc/xmlres.h>
#include "wx/config/option-proxy.h"
#include "wx/widgets/utils.h"
namespace dialogs {
namespace {
// Due to the way wxWidgets handles initialization, we need to use a custom
// persistent object to save and restore the dialog's position. In particular,
// when the dialog is constructed, the the main window is not (yet) in its final
// position. This means we need to differentiate between 2 states here:
// 1. The dialog was registered for the first time and never shown.
// We don't want to save the position in this case.
// 2. The dialog was registered and shown at least once.
// We want to save the position in this case. However, on restore, we need
// to check we are not drawing out of bounds.
//
// We can't use the built-in wxPersistenceManager for this, as it doesn't allow
// us to differentiate between these 2 states.
class PersistentBaseDialog : public wxPersistentWindow<BaseDialog> {
public:
PersistentBaseDialog(BaseDialog* window) : wxPersistentWindow<BaseDialog>(window) {}
private:
// wxPersistentObject implementation.
wxString GetKind() const override { return "Dialog"; }
void Save() const override {
if (!dialog_shown_) {
// Do not update the position if the dialog was not shown.
return;
}
const wxRect dialog_rect = this->Get()->GetRect();
this->SaveValue("x", dialog_rect.x);
this->SaveValue("y", dialog_rect.y);
this->SaveValue("width", dialog_rect.width);
this->SaveValue("height", dialog_rect.height);
}
bool Restore() override {
dialog_shown_ = true;
wxRect dialog_rect(0, 0, 0, 0);
if (!this->RestoreValue("x", &dialog_rect.x)) {
return false;
};
if (!this->RestoreValue("y", &dialog_rect.y)) {
return false;
};
if (!this->RestoreValue("width", &dialog_rect.width)) {
return false;
};
if (!this->RestoreValue("height", &dialog_rect.height)) {
return false;
};
this->Get()->SetSize(dialog_rect);
return true;
}
bool dialog_shown_ = false;
};
} // namespace
// static
wxDialog* BaseDialog::LoadDialog(wxWindow* parent, const wxString& xrc_file) {
assert(parent);
return new BaseDialog(parent, xrc_file);
}
BaseDialog::BaseDialog(wxWindow* parent, const wxString& xrc_file)
: 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
[[maybe_unused]] const bool success = wxXmlResource::Get()->LoadDialog(this, parent, xrc_file);
assert(success);
// Bind the event handler.
this->Bind(wxEVT_SHOW, &BaseDialog::OnBaseDialogShow, this);
wxPersistenceManager::Get().Register(this, new PersistentBaseDialog(this));
}
wxWindow* BaseDialog::GetValidatedChild(const wxString& name) const {
wxWindow* window = this->FindWindow(name);
assert(window);
return window;
}
void BaseDialog::OnBaseDialogShow(wxShowEvent& event) {
if (event.IsShown()) {
// Restore the dialog saved position.
if (wxPersistenceManager::Get().Restore(this)) {
// Ensure we are not restoring the dialog out of bounds.
if (!widgets::GetDisplayRect().Intersects(this->GetRect())) {
this->RepositionDialog();
}
} else {
// First-time use.
this->RepositionDialog();
}
// Do not run this again.
this->Unbind(wxEVT_SHOW, &BaseDialog::OnBaseDialogShow, this);
}
// Let the event propagate.
event.Skip();
}
void BaseDialog::RepositionDialog() {
// Re-position the dialog slightly to the bottom-right of the parent.
const wxWindow* parent = this->GetParent();
wxPoint parent_pos;
if (parent) {
parent_pos = parent->GetPosition();
} else {
parent_pos = wxPoint(OPTION(kGeomWindowX), OPTION(kGeomWindowY));
}
const wxPoint dialog_pos = parent_pos + wxPoint(40, 40);
this->SetPosition(dialog_pos);
}
} // namespace dialogs

View File

@ -0,0 +1,42 @@
#ifndef VBAM_WX_DIALOGS_BASE_DIALOG_H_
#define VBAM_WX_DIALOGS_BASE_DIALOG_H_
#include <wx/dialog.h>
#include <wx/event.h>
#include "wx/widgets/keep-on-top-styler.h"
namespace dialogs {
class BaseDialog : public wxDialog {
public:
static wxDialog* LoadDialog(wxWindow* parent, const wxString& xrc_file);
~BaseDialog() override = default;
protected:
BaseDialog(wxWindow* parent, const wxString& xrc_file);
// Helper function to assert on the returned value.
wxWindow* GetValidatedChild(const wxString& name) const;
template <class T>
T* GetValidatedChild(const wxString& name) const {
T* child = wxDynamicCast(this->GetValidatedChild(name), T);
assert(child);
return child;
}
private:
// Handler for the wxEVT_SHOW event.
void OnBaseDialogShow(wxShowEvent& event);
// Repositions the dialog to the bottom-right of the parent.
void RepositionDialog();
const widgets::KeepOnTopStyler keep_on_top_styler_;
};
} // namespace dialogs
#endif // VBAM_WX_DIALOGS_BASE_DIALOG_H_

View File

@ -2,9 +2,7 @@
#include <wx/filepicker.h> #include <wx/filepicker.h>
#include <wx/xrc/xmlres.h> #include "wx/dialogs/base-dialog.h"
#include "wx/dialogs/validated-child.h"
#include "wx/widgets/option-validator.h" #include "wx/widgets/option-validator.h"
namespace dialogs { namespace dialogs {
@ -58,29 +56,23 @@ DirectoriesConfig* DirectoriesConfig::NewInstance(wxWindow* parent) {
return new DirectoriesConfig(parent); return new DirectoriesConfig(parent);
} }
DirectoriesConfig::DirectoriesConfig(wxWindow* parent) DirectoriesConfig::DirectoriesConfig(wxWindow* parent) : BaseDialog(parent, "DirectoriesConfig") {
: wxDialog(), keep_on_top_styler_(this) { // clang-format off
#if !wxCHECK_VERSION(3, 1, 0) SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("GBARoms"),
// 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, "DirectoriesConfig");
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "GBARoms"),
config::OptionID::kGBAROMDir); config::OptionID::kGBAROMDir);
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "GBRoms"), SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("GBRoms"),
config::OptionID::kGBROMDir); config::OptionID::kGBROMDir);
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "GBCRoms"), SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("GBCRoms"),
config::OptionID::kGBGBCROMDir); config::OptionID::kGBGBCROMDir);
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "BatSaves"), SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("BatSaves"),
config::OptionID::kGenBatteryDir); config::OptionID::kGenBatteryDir);
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "StateSaves"), SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("StateSaves"),
config::OptionID::kGenStateDir); config::OptionID::kGenStateDir);
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "Screenshots"), SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("Screenshots"),
config::OptionID::kGenScreenshotDir); config::OptionID::kGenScreenshotDir);
SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>(this, "Recordings"), SetUpDirPicker(GetValidatedChild<wxDirPickerCtrl>("Recordings"),
config::OptionID::kGenRecordingDir); config::OptionID::kGenRecordingDir);
// clang-format on
this->Fit(); this->Fit();
} }

View File

@ -1,14 +1,12 @@
#ifndef VBAM_WX_DIALOGS_DIRECTORIES_CONFIG_H_ #ifndef VBAM_WX_DIALOGS_DIRECTORIES_CONFIG_H_
#define VBAM_WX_DIALOGS_DIRECTORIES_CONFIG_H_ #define VBAM_WX_DIALOGS_DIRECTORIES_CONFIG_H_
#include <wx/dialog.h> #include "wx/dialogs/base-dialog.h"
#include "wx/widgets/keep-on-top-styler.h"
namespace dialogs { namespace dialogs {
// Manages the directories configuration dialog. // Manages the directories configuration dialog.
class DirectoriesConfig : public wxDialog { class DirectoriesConfig : public BaseDialog {
public: public:
static DirectoriesConfig* NewInstance(wxWindow* parent); static DirectoriesConfig* NewInstance(wxWindow* parent);
~DirectoriesConfig() override = default; ~DirectoriesConfig() override = default;
@ -18,8 +16,6 @@ private:
// static method. This is because this class is destroyed when its // static method. This is because this class is destroyed when its
// owner, `parent` is destroyed. This prevents accidental deletion. // owner, `parent` is destroyed. This prevents accidental deletion.
DirectoriesConfig(wxWindow* parent); DirectoriesConfig(wxWindow* parent);
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -16,7 +16,7 @@
#include "wx/config/option-id.h" #include "wx/config/option-id.h"
#include "wx/config/option-proxy.h" #include "wx/config/option-proxy.h"
#include "wx/config/option.h" #include "wx/config/option.h"
#include "wx/dialogs/validated-child.h" #include "wx/dialogs/base-dialog.h"
#include "wx/rpi.h" #include "wx/rpi.h"
#include "wx/widgets/option-validator.h" #include "wx/widgets/option-validator.h"
#include "wx/widgets/render-plugin.h" #include "wx/widgets/render-plugin.h"
@ -225,7 +225,7 @@ DisplayConfig* DisplayConfig::NewInstance(wxWindow* parent) {
} }
DisplayConfig::DisplayConfig(wxWindow* parent) DisplayConfig::DisplayConfig(wxWindow* parent)
: wxDialog(), : BaseDialog(parent, "DisplayConfig"),
filter_observer_(config::OptionID::kDispFilter, filter_observer_(config::OptionID::kDispFilter,
std::bind(&DisplayConfig::OnFilterChanged, std::bind(&DisplayConfig::OnFilterChanged,
this, this,
@ -233,77 +233,69 @@ DisplayConfig::DisplayConfig(wxWindow* parent)
interframe_observer_(config::OptionID::kDispIFB, interframe_observer_(config::OptionID::kDispIFB,
std::bind(&DisplayConfig::OnInterframeChanged, std::bind(&DisplayConfig::OnInterframeChanged,
this, this,
std::placeholders::_1)), std::placeholders::_1)) {
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, "DisplayConfig");
// Speed // Speed
GetValidatedChild(this, "FrameSkip") GetValidatedChild("FrameSkip")
->SetValidator( ->SetValidator(
widgets::OptionIntValidator(config::OptionID::kPrefFrameSkip)); widgets::OptionIntValidator(config::OptionID::kPrefFrameSkip));
// On-Screen Display // On-Screen Display
GetValidatedChild(this, "SpeedIndicator") GetValidatedChild("SpeedIndicator")
->SetValidator( ->SetValidator(
widgets::OptionChoiceValidator(config::OptionID::kPrefShowSpeed)); widgets::OptionChoiceValidator(config::OptionID::kPrefShowSpeed));
// Zoom // Zoom
GetValidatedChild(this, "DefaultScale")->SetValidator(ScaleValidator()); GetValidatedChild("DefaultScale")->SetValidator(ScaleValidator());
// this was a choice, but I'd rather not have to make an off-by-one // this was a choice, but I'd rather not have to make an off-by-one
// validator just for this, and spinctrl is good enough. // validator just for this, and spinctrl is good enough.
GetValidatedChild(this, "MaxScale") GetValidatedChild("MaxScale")
->SetValidator(wxGenericValidator(&gopts.max_scale)); ->SetValidator(wxGenericValidator(&gopts.max_scale));
// Basic // Basic
GetValidatedChild(this, "OutputSimple") GetValidatedChild("OutputSimple")
->SetValidator(RenderValidator(config::RenderMethod::kSimple)); ->SetValidator(RenderValidator(config::RenderMethod::kSimple));
#if defined(__WXMAC__) #if defined(__WXMAC__)
GetValidatedChild(this, "OutputQuartz2D") GetValidatedChild("OutputQuartz2D")
->SetValidator(RenderValidator(config::RenderMethod::kQuartz2d)); ->SetValidator(RenderValidator(config::RenderMethod::kQuartz2d));
#else #else
GetValidatedChild(this, "OutputQuartz2D")->Hide(); GetValidatedChild("OutputQuartz2D")->Hide();
#endif #endif
#ifdef NO_OGL #ifdef NO_OGL
GetValidatedChild(this, "OutputOpenGL")->Hide(); GetValidatedChild("OutputOpenGL")->Hide();
#elif defined(HAVE_WAYLAND_SUPPORT) && !defined(HAVE_WAYLAND_EGL) #elif defined(HAVE_WAYLAND_SUPPORT) && !defined(HAVE_WAYLAND_EGL)
// wxGLCanvas segfaults on Wayland before wx 3.2. // wxGLCanvas segfaults on Wayland before wx 3.2.
if (IsWayland()) { if (IsWayland()) {
GetValidatedChild(this, "OutputOpenGL")->Hide(); GetValidatedChild("OutputOpenGL")->Hide();
} else { } else {
GetValidatedChild(this, "OutputOpenGL") GetValidatedChild("OutputOpenGL")
->SetValidator(RenderValidator(config::RenderMethod::kOpenGL)); ->SetValidator(RenderValidator(config::RenderMethod::kOpenGL));
} }
#else #else
GetValidatedChild(this, "OutputOpenGL") GetValidatedChild("OutputOpenGL")
->SetValidator(RenderValidator(config::RenderMethod::kOpenGL)); ->SetValidator(RenderValidator(config::RenderMethod::kOpenGL));
#endif // NO_OGL #endif // NO_OGL
#if defined(__WXMSW__) && !defined(NO_D3D) #if defined(__WXMSW__) && !defined(NO_D3D)
// Enable the Direct3D option on Windows. // Enable the Direct3D option on Windows.
GetValidatedChild(this, "OutputDirect3D") GetValidatedChild("OutputDirect3D")
->SetValidator(RenderValidator(config::RenderMethod::kDirect3d)); ->SetValidator(RenderValidator(config::RenderMethod::kDirect3d));
#else #else
GetValidatedChild(this, "OutputDirect3D")->Hide(); GetValidatedChild("OutputDirect3D")->Hide();
#endif #endif
filter_selector_ = GetValidatedChild<wxChoice>(this, "Filter"); filter_selector_ = GetValidatedChild<wxChoice>("Filter");
filter_selector_->SetValidator(FilterValidator()); filter_selector_->SetValidator(FilterValidator());
filter_selector_->Bind(wxEVT_CHOICE, &DisplayConfig::UpdatePlugin, this, filter_selector_->Bind(wxEVT_CHOICE, &DisplayConfig::UpdatePlugin, this,
GetId()); GetId());
// These are filled and/or hidden at dialog load time. // These are filled and/or hidden at dialog load time.
plugin_label_ = GetValidatedChild<wxControl>(this, "PluginLab"); plugin_label_ = GetValidatedChild<wxControl>("PluginLab");
plugin_selector_ = GetValidatedChild<wxChoice>(this, "Plugin"); plugin_selector_ = GetValidatedChild<wxChoice>("Plugin");
interframe_selector_ = GetValidatedChild<wxChoice>(this, "IFB"); interframe_selector_ = GetValidatedChild<wxChoice>("IFB");
interframe_selector_->SetValidator(InterframeValidator()); interframe_selector_->SetValidator(InterframeValidator());
Bind(wxEVT_SHOW, &DisplayConfig::OnDialogShowEvent, this, GetId()); Bind(wxEVT_SHOW, &DisplayConfig::OnDialogShowEvent, this, GetId());

View File

@ -1,11 +1,8 @@
#ifndef VBAM_WX_DIALOGS_DISPLAY_CONFIG_H_ #ifndef VBAM_WX_DIALOGS_DISPLAY_CONFIG_H_
#define VBAM_WX_DIALOGS_DISPLAY_CONFIG_H_ #define VBAM_WX_DIALOGS_DISPLAY_CONFIG_H_
#include <wx/dialog.h> #include "wx/dialogs/base-dialog.h"
#include <wx/event.h>
#include "wx/config/option-observer.h" #include "wx/config/option-observer.h"
#include "wx/widgets/keep-on-top-styler.h"
// Forward declarations. // Forward declarations.
class wxChoice; class wxChoice;
@ -19,7 +16,7 @@ class Option;
namespace dialogs { namespace dialogs {
// Manages the display configuration dialog. // Manages the display configuration dialog.
class DisplayConfig : public wxDialog { class DisplayConfig : public BaseDialog {
public: public:
static DisplayConfig* NewInstance(wxWindow* parent); static DisplayConfig* NewInstance(wxWindow* parent);
~DisplayConfig() override = default; ~DisplayConfig() override = default;
@ -58,7 +55,6 @@ private:
wxChoice* interframe_selector_; wxChoice* interframe_selector_;
const config::OptionsObserver filter_observer_; const config::OptionsObserver filter_observer_;
const config::OptionsObserver interframe_observer_; const config::OptionsObserver interframe_observer_;
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -15,8 +15,9 @@
#include "wx/config/option-observer.h" #include "wx/config/option-observer.h"
#include "wx/config/option-proxy.h" #include "wx/config/option-proxy.h"
#include "wx/dialogs/validated-child.h" #include "wx/dialogs/base-dialog.h"
#include "wx/widgets/option-validator.h" #include "wx/widgets/option-validator.h"
#include "wx/widgets/utils.h"
namespace dialogs { namespace dialogs {
@ -221,23 +222,22 @@ private:
GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id) GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id)
: wxClientData(), : wxClientData(),
default_selector_(GetValidatedChild<wxChoice>(panel, "DefaultPalette")), default_selector_(widgets::GetValidatedChild<wxChoice>(panel, "DefaultPalette")),
option_id_(static_cast<config::OptionID>( option_id_(static_cast<config::OptionID>(static_cast<size_t>(config::OptionID::kGBPalette0) +
static_cast<size_t>(config::OptionID::kGBPalette0) + palette_id)) { palette_id)) {
assert(panel); assert(panel);
assert(palette_id < kNbPalettes); assert(palette_id < kNbPalettes);
default_selector_->Bind( default_selector_->Bind(
wxEVT_CHOICE, &GBPalettePanelData::OnDefaultPaletteSelected, this); wxEVT_CHOICE, &GBPalettePanelData::OnDefaultPaletteSelected, this);
GetValidatedChild<wxCheckBox>(panel, "UsePalette") widgets::GetValidatedChild<wxCheckBox>(panel, "UsePalette")
->SetValidator(widgets::OptionSelectedValidator( ->SetValidator(
config::OptionID::kPrefGBPaletteOption, palette_id)); widgets::OptionSelectedValidator(config::OptionID::kPrefGBPaletteOption, palette_id));
for (size_t i = 0; i < colour_pickers_.size(); i++) { for (size_t i = 0; i < colour_pickers_.size(); i++) {
wxColourPickerCtrl* colour_picker = wxColourPickerCtrl* colour_picker =
GetValidatedChild<wxColourPickerCtrl>( widgets::GetValidatedChild<wxColourPickerCtrl>(panel, wxString::Format("Color%zu", i));
panel, wxString::Format("Color%zu", i));
colour_pickers_[i] = colour_picker; colour_pickers_[i] = colour_picker;
// Update the internal palette reference on colour change. // Update the internal palette reference on colour change.
@ -247,7 +247,7 @@ GBPalettePanelData::GBPalettePanelData(wxPanel* panel, size_t palette_id)
colour_picker->GetId()); colour_picker->GetId());
} }
GetValidatedChild(panel, "Reset") widgets::GetValidatedChild(panel, "Reset")
->Bind(wxEVT_BUTTON, &GBPalettePanelData::OnPaletteReset, this); ->Bind(wxEVT_BUTTON, &GBPalettePanelData::OnPaletteReset, this);
} }
@ -317,39 +317,27 @@ GameBoyConfig* GameBoyConfig::NewInstance(wxWindow* parent) {
return new GameBoyConfig(parent); return new GameBoyConfig(parent);
} }
GameBoyConfig::GameBoyConfig(wxWindow* parent) GameBoyConfig::GameBoyConfig(wxWindow* parent) : BaseDialog(parent, "GameBoyConfig") {
: 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. // System and Peripherals.
GetValidatedChild(this, "System") GetValidatedChild("System")->SetValidator(
->SetValidator(widgets::OptionChoiceValidator( widgets::OptionChoiceValidator(config::OptionID::kPrefEmulatorType));
config::OptionID::kPrefEmulatorType));
// "Display borders" corresponds to 2 variables. // "Display borders" corresponds to 2 variables.
GetValidatedChild(this, "Borders")->SetValidator(BorderSelectorValidator()); GetValidatedChild("Borders")->SetValidator(BorderSelectorValidator());
// GB BIOS ROM // GB BIOS ROM
GetValidatedChild(this, "GBBiosPicker") GetValidatedChild("GBBiosPicker")
->SetValidator(BIOSPickerValidator( ->SetValidator(BIOSPickerValidator(config::OptionID::kGBBiosFile,
config::OptionID::kGBBiosFile, GetValidatedChild<wxStaticText>("GBBiosLabel")));
GetValidatedChild<wxStaticText>(this, "GBBiosLabel")));
// GBC BIOS ROM // GBC BIOS ROM
GetValidatedChild(this, "GBCBiosPicker") GetValidatedChild("GBCBiosPicker")
->SetValidator(BIOSPickerValidator( ->SetValidator(BIOSPickerValidator(config::OptionID::kGBGBCBiosFile,
config::OptionID::kGBGBCBiosFile, GetValidatedChild<wxStaticText>("GBCBiosLabel")));
GetValidatedChild<wxStaticText>(this, "GBCBiosLabel")));
for (size_t i = 0; i < kNbPalettes; i++) { for (size_t i = 0; i < kNbPalettes; i++) {
// All of the wxPanel logic is handled in its client object. // All of the wxPanel logic is handled in its client object.
wxPanel* panel = wxPanel* panel = GetValidatedChild<wxPanel>(wxString::Format("cp%zu", i));
GetValidatedChild<wxPanel>(this, wxString::Format("cp%zu", i));
GBPalettePanelData* palette_data = new GBPalettePanelData(panel, i); GBPalettePanelData* palette_data = new GBPalettePanelData(panel, i);
// `panel` takes ownership of `palette_data` here. // `panel` takes ownership of `palette_data` here.

View File

@ -2,14 +2,13 @@
#define VBAM_WX_DIALOGS_GAME_BOY_CONFIG_H_ #define VBAM_WX_DIALOGS_GAME_BOY_CONFIG_H_
#include <wx/clrpicker.h> #include <wx/clrpicker.h>
#include <wx/dialog.h>
#include "wx/widgets/keep-on-top-styler.h" #include "wx/dialogs/base-dialog.h"
namespace dialogs { namespace dialogs {
// Manages the Game Boy configuration dialog. // Manages the Game Boy configuration dialog.
class GameBoyConfig : public wxDialog { class GameBoyConfig : public BaseDialog {
public: public:
static GameBoyConfig* NewInstance(wxWindow* parent); static GameBoyConfig* NewInstance(wxWindow* parent);
~GameBoyConfig() override = default; ~GameBoyConfig() override = default;
@ -19,8 +18,6 @@ private:
// static method. This is because this class is destroyed when its // static method. This is because this class is destroyed when its
// owner, `parent` is destroyed. This prevents accidental deletion. // owner, `parent` is destroyed. This prevents accidental deletion.
GameBoyConfig(wxWindow* parent); GameBoyConfig(wxWindow* parent);
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -5,8 +5,8 @@
#include "core/base/sizes.h" #include "core/base/sizes.h"
#include "core/gb/gb.h" #include "core/gb/gb.h"
#include "wx/dialogs/base-dialog.h"
#include "wx/dialogs/game-maker.h" #include "wx/dialogs/game-maker.h"
#include "wx/dialogs/validated-child.h"
namespace dialogs { namespace dialogs {
@ -171,14 +171,7 @@ GbRomInfo* GbRomInfo::NewInstance(wxWindow* parent) {
return new GbRomInfo(parent); return new GbRomInfo(parent);
} }
GbRomInfo::GbRomInfo(wxWindow* parent) : wxDialog(), keep_on_top_styler_(this) { GbRomInfo::GbRomInfo(wxWindow* parent) : BaseDialog(parent, "GBROMInfo") {
#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, "GBROMInfo");
Bind(wxEVT_SHOW, &GbRomInfo::OnDialogShowEvent, this); Bind(wxEVT_SHOW, &GbRomInfo::OnDialogShowEvent, this);
} }
@ -191,23 +184,23 @@ void GbRomInfo::OnDialogShowEvent(wxShowEvent& event) {
} }
// Populate the dialog. // Populate the dialog.
GetValidatedChild(this, "Title")->SetLabel(g_gbCartData.title()); GetValidatedChild("Title")->SetLabel(g_gbCartData.title());
GetValidatedChild(this, "MakerCode")->SetLabel(g_gbCartData.maker_code()); GetValidatedChild("MakerCode")->SetLabel(g_gbCartData.maker_code());
GetValidatedChild(this, "MakerName")->SetLabel(GetGameMakerName(g_gbCartData.maker_code())); GetValidatedChild("MakerName")->SetLabel(GetGameMakerName(g_gbCartData.maker_code()));
GetValidatedChild(this, "CartridgeType")->SetLabel(GetCartType()); GetValidatedChild("CartridgeType")->SetLabel(GetCartType());
GetValidatedChild(this, "SGBCode")->SetLabel(GetCartSGBFlag()); GetValidatedChild("SGBCode")->SetLabel(GetCartSGBFlag());
GetValidatedChild(this, "CGBCode")->SetLabel(GetCartCGBFlag()); GetValidatedChild("CGBCode")->SetLabel(GetCartCGBFlag());
GetValidatedChild(this, "ROMSize")->SetLabel(GetCartRomSize()); GetValidatedChild("ROMSize")->SetLabel(GetCartRomSize());
GetValidatedChild(this, "RAMSize")->SetLabel(GetCartRamSize()); GetValidatedChild("RAMSize")->SetLabel(GetCartRamSize());
GetValidatedChild(this, "DestCode")->SetLabel(GetCartDestinationCode()); GetValidatedChild("DestCode")->SetLabel(GetCartDestinationCode());
GetValidatedChild(this, "LicCode") GetValidatedChild("LicCode")
->SetLabel(wxString::Format("%02X", g_gbCartData.old_licensee_code())); ->SetLabel(wxString::Format("%02X", g_gbCartData.old_licensee_code()));
GetValidatedChild(this, "Version") GetValidatedChild("Version")
->SetLabel(wxString::Format("%02X", g_gbCartData.version_flag())); ->SetLabel(wxString::Format("%02X", g_gbCartData.version_flag()));
GetValidatedChild(this, "HeaderChecksum") GetValidatedChild("HeaderChecksum")
->SetLabel(wxString::Format(_("%02X (Actual: %02X)"), g_gbCartData.header_checksum(), ->SetLabel(wxString::Format(_("%02X (Actual: %02X)"), g_gbCartData.header_checksum(),
g_gbCartData.actual_header_checksum())); g_gbCartData.actual_header_checksum()));
GetValidatedChild(this, "CartridgeChecksum") GetValidatedChild("CartridgeChecksum")
->SetLabel(wxString::Format(_("%04X (Actual: %04X)"), g_gbCartData.global_checksum(), ->SetLabel(wxString::Format(_("%04X (Actual: %04X)"), g_gbCartData.global_checksum(),
g_gbCartData.actual_global_checksum())); g_gbCartData.actual_global_checksum()));

View File

@ -1,13 +1,11 @@
#ifndef VBAM_WX_DIALOGS_GB_ROM_INFO_H_ #ifndef VBAM_WX_DIALOGS_GB_ROM_INFO_H_
#define VBAM_WX_DIALOGS_GB_ROM_INFO_H_ #define VBAM_WX_DIALOGS_GB_ROM_INFO_H_
#include <wx/dialog.h> #include "wx/dialogs/base-dialog.h"
#include "wx/widgets/keep-on-top-styler.h"
namespace dialogs { namespace dialogs {
class GbRomInfo : public wxDialog { class GbRomInfo : public BaseDialog {
public: public:
static GbRomInfo* NewInstance(wxWindow* parent); static GbRomInfo* NewInstance(wxWindow* parent);
~GbRomInfo() override = default; ~GbRomInfo() override = default;
@ -20,8 +18,6 @@ private:
// Handler for the wxEVT_SHOW event. // Handler for the wxEVT_SHOW event.
void OnDialogShowEvent(wxShowEvent& event); void OnDialogShowEvent(wxShowEvent& event);
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -2,11 +2,11 @@
#include <wx/xrc/xmlres.h> #include <wx/xrc/xmlres.h>
#include "wx/dialogs/validated-child.h"
#include "wx/config/option-proxy.h" #include "wx/config/option-proxy.h"
#include "wx/config/option.h" #include "wx/dialogs/base-dialog.h"
#include "wx/widgets/option-validator.h" #include "wx/widgets/option-validator.h"
#include "wx/widgets/user-input-ctrl.h" #include "wx/widgets/user-input-ctrl.h"
#include "wx/widgets/utils.h"
#include "wx/wxvbam.h" #include "wx/wxvbam.h"
namespace dialogs { namespace dialogs {
@ -17,23 +17,17 @@ JoypadConfig* JoypadConfig::NewInstance(wxWindow* parent) {
return new JoypadConfig(parent); return new JoypadConfig(parent);
} }
JoypadConfig::JoypadConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(this) { JoypadConfig::JoypadConfig(wxWindow* parent) : BaseDialog(parent, "JoypadConfig") {
#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, "JoypadConfig");
this->Bind(wxEVT_CHECKBOX, std::bind(&JoypadConfig::ToggleSDLGameControllerMode, this), this->Bind(wxEVT_CHECKBOX, std::bind(&JoypadConfig::ToggleSDLGameControllerMode, this),
XRCID("SDLGameControllerMode")); XRCID("SDLGameControllerMode"));
GetValidatedChild<wxCheckBox>(this, "SDLGameControllerMode")->SetValue(OPTION(kSDLGameControllerMode)); GetValidatedChild<wxCheckBox>("SDLGameControllerMode")
->SetValue(OPTION(kSDLGameControllerMode));
for (int joypad = 0; joypad < 4; joypad++) { for (int joypad = 0; joypad < 4; joypad++) {
wxWindow* panel = GetValidatedChild(this, wxString::Format("joy%d", joypad + 1)); wxWindow* panel = GetValidatedChild(wxString::Format("joy%d", joypad + 1));
GetValidatedChild(panel, "DefaultConfig") widgets::GetValidatedChild(panel, "DefaultConfig")
->SetValidator( ->SetValidator(
widgets::OptionSelectedValidator(config::OptionID::kJoyDefault, joypad + 1)); widgets::OptionSelectedValidator(config::OptionID::kJoyDefault, joypad + 1));
@ -45,7 +39,7 @@ JoypadConfig::JoypadConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(t
for (const config::GameKey& game_key : config::kAllGameKeys) { for (const config::GameKey& game_key : config::kAllGameKeys) {
const wxString game_key_name = config::GameKeyToString(game_key); const wxString game_key_name = config::GameKeyToString(game_key);
widgets::UserInputCtrl* game_key_control = widgets::UserInputCtrl* game_key_control =
GetValidatedChild<widgets::UserInputCtrl>(panel, game_key_name); widgets::GetValidatedChild<widgets::UserInputCtrl>(panel, game_key_name);
wxWindow* current_parent = game_key_control->GetParent(); wxWindow* current_parent = game_key_control->GetParent();
game_key_control->SetValidator( game_key_control->SetValidator(
@ -77,28 +71,28 @@ JoypadConfig::JoypadConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(t
void JoypadConfig::ResetToDefaults(wxWindow* panel) { void JoypadConfig::ResetToDefaults(wxWindow* panel) {
for (const config::GameKey& game_key : config::kAllGameKeys) { for (const config::GameKey& game_key : config::kAllGameKeys) {
GetValidatedChild<widgets::UserInputCtrl>(panel, config::GameKeyToString(game_key)) widgets::GetValidatedChild<widgets::UserInputCtrl>(panel, config::GameKeyToString(game_key))
->SetInputs(kDefaultBindings.find(config::GameControl(0, game_key))->second); ->SetInputs(kDefaultBindings.find(config::GameControl(0, game_key))->second);
} }
} }
void JoypadConfig::ClearJoypad(wxWindow* panel) { void JoypadConfig::ClearJoypad(wxWindow* panel) {
for (const config::GameKey& game_key : config::kAllGameKeys) { for (const config::GameKey& game_key : config::kAllGameKeys) {
GetValidatedChild<widgets::UserInputCtrl>(panel, config::GameKeyToString(game_key)) widgets::GetValidatedChild<widgets::UserInputCtrl>(panel, config::GameKeyToString(game_key))
->Clear(); ->Clear();
} }
} }
void JoypadConfig::ToggleSDLGameControllerMode() { void JoypadConfig::ToggleSDLGameControllerMode() {
OPTION(kSDLGameControllerMode) = GetValidatedChild<wxCheckBox>(this, "SDLGameControllerMode") OPTION(kSDLGameControllerMode) =
->IsChecked(); GetValidatedChild<wxCheckBox>("SDLGameControllerMode")->IsChecked();
ClearAllJoypads(); ClearAllJoypads();
wxGetApp().frame->PollAllJoysticks(); wxGetApp().frame->PollAllJoysticks();
} }
void JoypadConfig::ClearAllJoypads() { void JoypadConfig::ClearAllJoypads() {
for (unsigned joypad = 0; joypad < 4; joypad++) { for (unsigned joypad = 0; joypad < 4; joypad++) {
wxWindow* panel = GetValidatedChild(this, wxString::Format("joy%d", joypad + 1)); wxWindow* panel = GetValidatedChild(wxString::Format("joy%d", joypad + 1));
ClearJoypad(panel); ClearJoypad(panel);
} }

View File

@ -1,14 +1,11 @@
#ifndef VBAM_WX_DIALOGS_JOYPAD_CONFIG_H_ #ifndef VBAM_WX_DIALOGS_JOYPAD_CONFIG_H_
#define VBAM_WX_DIALOGS_JOYPAD_CONFIG_H_ #define VBAM_WX_DIALOGS_JOYPAD_CONFIG_H_
#include <wx/dialog.h> #include "wx/dialogs/base-dialog.h"
#include "wx/widgets/keep-on-top-styler.h"
namespace dialogs { namespace dialogs {
// Manages the Joypad configuration dialog. // Manages the Joypad configuration dialog.
class JoypadConfig : public wxDialog { class JoypadConfig : public BaseDialog {
public: public:
static JoypadConfig* NewInstance(wxWindow* parent); static JoypadConfig* NewInstance(wxWindow* parent);
~JoypadConfig() override = default; ~JoypadConfig() override = default;
@ -30,8 +27,6 @@ private:
// Toggle SDL GameController mode for all joysticks. // Toggle SDL GameController mode for all joysticks.
void ToggleSDLGameControllerMode(); void ToggleSDLGameControllerMode();
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -16,7 +16,7 @@
#include "wx/config/option-id.h" #include "wx/config/option-id.h"
#include "wx/config/option-proxy.h" #include "wx/config/option-proxy.h"
#include "wx/config/option.h" #include "wx/config/option.h"
#include "wx/dialogs/validated-child.h" #include "wx/dialogs/base-dialog.h"
#include "wx/widgets/option-validator.h" #include "wx/widgets/option-validator.h"
namespace dialogs { namespace dialogs {
@ -140,31 +140,24 @@ SoundConfig* SoundConfig::NewInstance(wxWindow* parent) {
return new SoundConfig(parent); return new SoundConfig(parent);
} }
SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(this) { SoundConfig::SoundConfig(wxWindow* parent) : BaseDialog(parent, "SoundConfig") {
#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, "SoundConfig");
// Volume slider configuration. // Volume slider configuration.
wxSlider* volume_slider = GetValidatedChild<wxSlider>(this, "Volume"); wxSlider* volume_slider = GetValidatedChild<wxSlider>("Volume");
volume_slider->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundVolume)); volume_slider->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundVolume));
GetValidatedChild(this, "Volume100") GetValidatedChild("Volume100")
->Bind(wxEVT_BUTTON, std::bind(&wxSlider::SetValue, volume_slider, 100)); ->Bind(wxEVT_BUTTON, std::bind(&wxSlider::SetValue, volume_slider, 100));
// Sound quality. // Sound quality.
GetValidatedChild(this, "Rate")->SetValidator(SoundRateValidator()); GetValidatedChild("Rate")->SetValidator(SoundRateValidator());
// Audio API selection. // Audio API selection.
wxWindow* audio_api_button = GetValidatedChild(this, "OpenAL"); wxWindow* audio_api_button = GetValidatedChild("OpenAL");
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kOpenAL)); audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kOpenAL));
audio_api_button->Bind(wxEVT_RADIOBUTTON, audio_api_button->Bind(wxEVT_RADIOBUTTON,
std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1, std::bind(&SoundConfig::OnAudioApiChanged, this, std::placeholders::_1,
config::AudioApi::kOpenAL)); config::AudioApi::kOpenAL));
audio_api_button = GetValidatedChild(this, "DirectSound"); audio_api_button = GetValidatedChild("DirectSound");
#if defined(__WXMSW__) #if defined(__WXMSW__)
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kDirectSound)); audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kDirectSound));
audio_api_button->Bind(wxEVT_RADIOBUTTON, audio_api_button->Bind(wxEVT_RADIOBUTTON,
@ -174,7 +167,7 @@ SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(thi
audio_api_button->Hide(); audio_api_button->Hide();
#endif #endif
audio_api_button = GetValidatedChild(this, "XAudio2"); audio_api_button = GetValidatedChild("XAudio2");
#if defined(VBAM_ENABLE_XAUDIO2) #if defined(VBAM_ENABLE_XAUDIO2)
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kXAudio2)); audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kXAudio2));
audio_api_button->Bind(wxEVT_RADIOBUTTON, audio_api_button->Bind(wxEVT_RADIOBUTTON,
@ -184,7 +177,7 @@ SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(thi
audio_api_button->Hide(); audio_api_button->Hide();
#endif #endif
audio_api_button = GetValidatedChild(this, "FAudio"); audio_api_button = GetValidatedChild("FAudio");
#if defined(VBAM_ENABLE_FAUDIO) #if defined(VBAM_ENABLE_FAUDIO)
audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kFAudio)); audio_api_button->SetValidator(AudioApiValidator(config::AudioApi::kFAudio));
audio_api_button->Bind(wxEVT_RADIOBUTTON, audio_api_button->Bind(wxEVT_RADIOBUTTON,
@ -195,7 +188,7 @@ SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(thi
#endif #endif
// Upmix configuration. // Upmix configuration.
upmix_checkbox_ = GetValidatedChild<wxCheckBox>(this, "Upmix"); upmix_checkbox_ = GetValidatedChild<wxCheckBox>("Upmix");
#if defined(VBAM_ENABLE_XAUDIO2) || defined(VBAM_ENABLE_FAUDIO) #if defined(VBAM_ENABLE_XAUDIO2) || defined(VBAM_ENABLE_FAUDIO)
upmix_checkbox_->SetValidator(widgets::OptionBoolValidator(config::OptionID::kSoundUpmix)); upmix_checkbox_->SetValidator(widgets::OptionBoolValidator(config::OptionID::kSoundUpmix));
#else #else
@ -203,7 +196,7 @@ SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(thi
#endif #endif
// DSound HW acceleration. // DSound HW acceleration.
hw_accel_checkbox_ = GetValidatedChild<wxCheckBox>(this, "HWAccel"); hw_accel_checkbox_ = GetValidatedChild<wxCheckBox>("HWAccel");
#if defined(__WXMSW__) #if defined(__WXMSW__)
hw_accel_checkbox_->SetValidator( hw_accel_checkbox_->SetValidator(
widgets::OptionBoolValidator(config::OptionID::kSoundDSoundHWAccel)); widgets::OptionBoolValidator(config::OptionID::kSoundDSoundHWAccel));
@ -212,23 +205,23 @@ SoundConfig::SoundConfig(wxWindow* parent) : wxDialog(), keep_on_top_styler_(thi
#endif #endif
// Buffers configuration. // Buffers configuration.
buffers_info_label_ = GetValidatedChild<wxControl>(this, "BuffersInfo"); buffers_info_label_ = GetValidatedChild<wxControl>("BuffersInfo");
buffers_slider_ = GetValidatedChild<wxSlider>(this, "Buffers"); buffers_slider_ = GetValidatedChild<wxSlider>("Buffers");
buffers_slider_->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundBuffers)); buffers_slider_->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundBuffers));
buffers_slider_->Bind(wxEVT_SLIDER, &SoundConfig::OnBuffersChanged, this); buffers_slider_->Bind(wxEVT_SLIDER, &SoundConfig::OnBuffersChanged, this);
// Game Boy configuration. // Game Boy configuration.
GetValidatedChild(this, "GBEcho") GetValidatedChild("GBEcho")->SetValidator(
->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBEcho)); widgets::OptionIntValidator(config::OptionID::kSoundGBEcho));
GetValidatedChild(this, "GBStereo") GetValidatedChild("GBStereo")
->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBStereo)); ->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBStereo));
// Game Boy Advance configuration. // Game Boy Advance configuration.
GetValidatedChild(this, "GBASoundFiltering") GetValidatedChild("GBASoundFiltering")
->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBAFiltering)); ->SetValidator(widgets::OptionIntValidator(config::OptionID::kSoundGBAFiltering));
// Audio Device configuration. // Audio Device configuration.
audio_device_selector_ = GetValidatedChild<wxChoice>(this, "Device"); audio_device_selector_ = GetValidatedChild<wxChoice>("Device");
audio_device_selector_->SetValidator(AudioDeviceValidator()); audio_device_selector_->SetValidator(AudioDeviceValidator());
this->Bind(wxEVT_SHOW, &SoundConfig::OnShow, this); this->Bind(wxEVT_SHOW, &SoundConfig::OnShow, this);

View File

@ -1,11 +1,8 @@
#ifndef VBAM_WX_DIALOGS_SOUND_CONFIG_H_ #ifndef VBAM_WX_DIALOGS_SOUND_CONFIG_H_
#define VBAM_WX_DIALOGS_SOUND_CONFIG_H_ #define VBAM_WX_DIALOGS_SOUND_CONFIG_H_
#include <wx/dialog.h>
#include <wx/event.h>
#include "wx/config/option.h" #include "wx/config/option.h"
#include "wx/widgets/keep-on-top-styler.h" #include "wx/dialogs/base-dialog.h"
// Forward declarations. // Forward declarations.
class wxChoice; class wxChoice;
@ -17,7 +14,7 @@ class wxWindow;
namespace dialogs { namespace dialogs {
// Manages the sound configuration dialog. // Manages the sound configuration dialog.
class SoundConfig : public wxDialog { class SoundConfig : public BaseDialog {
public: public:
static SoundConfig* NewInstance(wxWindow* parent); static SoundConfig* NewInstance(wxWindow* parent);
~SoundConfig() override = default; ~SoundConfig() override = default;
@ -42,7 +39,6 @@ private:
wxCheckBox* upmix_checkbox_; wxCheckBox* upmix_checkbox_;
wxCheckBox* hw_accel_checkbox_; wxCheckBox* hw_accel_checkbox_;
config::AudioApi current_audio_api_; config::AudioApi current_audio_api_;
const widgets::KeepOnTopStyler keep_on_top_styler_;
}; };
} // namespace dialogs } // namespace dialogs

View File

@ -40,6 +40,7 @@
#include "core/gba/gbaGlobals.h" #include "core/gba/gbaGlobals.h"
#include "wx/config/option-proxy.h" #include "wx/config/option-proxy.h"
#include "wx/dialogs/accel-config.h" #include "wx/dialogs/accel-config.h"
#include "wx/dialogs/base-dialog.h"
#include "wx/dialogs/directories-config.h" #include "wx/dialogs/directories-config.h"
#include "wx/dialogs/display-config.h" #include "wx/dialogs/display-config.h"
#include "wx/dialogs/game-boy-config.h" #include "wx/dialogs/game-boy-config.h"
@ -64,26 +65,8 @@ const
#undef wxvbam #undef wxvbam
#endif #endif
// this is supposed to happen automatically if a parent is marked recursive
// but some dialogs don't do it (propertydialog?)
// so go ahead and mark all dialogs for fully recursive validation
static void
mark_recursive(wxWindowBase* w)
{
w->SetExtraStyle(w->GetExtraStyle() | wxWS_EX_VALIDATE_RECURSIVELY);
wxWindowList l = w->GetChildren();
for (wxWindowList::iterator ch = l.begin(); ch != l.end(); ++ch)
mark_recursive(*ch);
}
#if (wxMAJOR_VERSION < 3)
#define GetXRCDialog(n) \
wxStaticCast(wxGetApp().frame->FindWindow(XRCID(n)), wxDialog)
#else
#define GetXRCDialog(n) \ #define GetXRCDialog(n) \
wxStaticCast(wxGetApp().frame->FindWindowByName(n), wxDialog) wxStaticCast(wxGetApp().frame->FindWindowByName(n), wxDialog)
#endif
// Event handlers must be methods of wxEvtHandler-derived objects // Event handlers must be methods of wxEvtHandler-derived objects
@ -343,11 +326,6 @@ public:
wxDialog* subdlg = GetXRCDialog("CheatEdit"); wxDialog* subdlg = GetXRCDialog("CheatEdit");
dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER); dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER);
if (OPTION(kDispKeepOnTop))
subdlg->SetWindowStyle(subdlg->GetWindowStyle() | wxSTAY_ON_TOP);
else
subdlg->SetWindowStyle(subdlg->GetWindowStyle() & ~wxSTAY_ON_TOP);
subdlg->ShowModal(); subdlg->ShowModal();
AddCheat(); AddCheat();
Reload(ncheats); Reload(ncheats);
@ -566,11 +544,6 @@ public:
wxDialog* subdlg = GetXRCDialog("CheatEdit"); wxDialog* subdlg = GetXRCDialog("CheatEdit");
dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER); dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER);
if (OPTION(kDispKeepOnTop))
subdlg->SetWindowStyle(subdlg->GetWindowStyle() | wxSTAY_ON_TOP);
else
subdlg->SetWindowStyle(subdlg->GetWindowStyle() & ~wxSTAY_ON_TOP);
if (subdlg->ShowModal() != wxID_OK) if (subdlg->ShowModal() != wxID_OK)
return; return;
@ -1098,11 +1071,6 @@ public:
wxDialog* subdlg = GetXRCDialog("CheatAdd"); wxDialog* subdlg = GetXRCDialog("CheatAdd");
dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER); dlg->SetWindowStyle(wxCAPTION | wxRESIZE_BORDER);
if (OPTION(kDispKeepOnTop))
subdlg->SetWindowStyle(subdlg->GetWindowStyle() | wxSTAY_ON_TOP);
else
subdlg->SetWindowStyle(subdlg->GetWindowStyle() & ~wxSTAY_ON_TOP);
if (subdlg->ShowModal() != wxID_OK) if (subdlg->ShowModal() != wxID_OK)
return; return;
@ -1629,38 +1597,8 @@ void CheckThrowXRCError(T pointer, const char* name)
wxDialog* MainFrame::LoadXRCDialog(const char* name) wxDialog* MainFrame::LoadXRCDialog(const char* name)
{ {
wxString dname = wxString::FromUTF8(name); wxString dname = wxString::FromUTF8(name);
wxDialog* dialog = wxXmlResource::Get()->LoadDialog(this, dname); wxDialog* dialog = dialogs::BaseDialog::LoadDialog(this, dname);
CheckThrowXRCError(dialog, name); CheckThrowXRCError(dialog, name);
/* wx-2.9.1 doesn't set parent for propertysheetdialogs for some reason */
/* this will generate a gtk warning but it is necessary for later */
/* retrieval using FindWindow() */
#if (wxMAJOR_VERSION < 3)
if (!dialog->GetParent())
dialog->Reparent(this);
#endif
mark_recursive(dialog);
return dialog;
}
wxDialog* MainFrame::LoadXRCropertySheetDialog(const char* name)
{
wxString dname = wxString::FromUTF8(name);
//Seems like the only way to do this
wxObject* anObject = wxXmlResource::Get()->LoadObject(this, dname, wxEmptyString);
wxDialog* dialog = dynamic_cast<wxDialog*>(anObject);
CheckThrowXRCError(dialog, name);
/* wx-2.9.1 doesn't set parent for propertysheetdialogs for some reason */
/* this will generate a gtk warning but it is necessary for later */
/* retrieval using FindWindow() */
#if (wxMAJOR_VERSION < 3)
if (!dialog->GetParent())
dialog->Reparent(this);
#endif
mark_recursive(dialog);
return dialog; return dialog;
} }
@ -2488,7 +2426,7 @@ bool MainFrame::BindControls()
fp->SetValidator(wxFileDirPickerValidator(&o, l)); \ fp->SetValidator(wxFileDirPickerValidator(&o, l)); \
} while (0) } while (0)
dialogs::GameBoyConfig::NewInstance(this); dialogs::GameBoyConfig::NewInstance(this);
d = LoadXRCropertySheetDialog("GameBoyAdvanceConfig"); d = LoadXRCDialog("GameBoyAdvanceConfig");
{ {
/// System and peripherals /// System and peripherals
ch = GetValidatedChild<wxChoice, wxGenericValidator>(d, "SaveType", wxGenericValidator(&coreOptions.cpuSaveType)); ch = GetValidatedChild<wxChoice, wxGenericValidator>(d, "SaveType", wxGenericValidator(&coreOptions.cpuSaveType));
@ -2554,7 +2492,7 @@ bool MainFrame::BindControls()
// at popup time. // at popup time.
// The only one that can only be popped up once is logging, so allocate // The only one that can only be popped up once is logging, so allocate
// and check it already. // and check it already.
logdlg = new LogDialog; logdlg = std::make_unique<LogDialog>();
// activate OnDropFile event handler // activate OnDropFile event handler
#if !defined(__WXGTK__) || wxCHECK_VERSION(2, 8, 10) #if !defined(__WXGTK__) || wxCHECK_VERSION(2, 8, 10)
// may not actually do anything, but verfied to work w/ Linux/Nautilus // may not actually do anything, but verfied to work w/ Linux/Nautilus

View File

@ -295,12 +295,14 @@ void load_opts(bool first_time_launch) {
for (cont = cfg->GetFirstGroup(s, grp_idx); cont; for (cont = cfg->GetFirstGroup(s, grp_idx); cont;
cont = cfg->GetNextGroup(s, grp_idx)) { cont = cfg->GetNextGroup(s, grp_idx)) {
// ignore wxWidgets-managed global library settings // ignore wxWidgets-managed global library settings
if (s == wxT("wxWindows")) if (s == "Persistent_Options") {
continue; continue;
}
// ignore file history // ignore file history
if (s == wxT("Recent")) if (s == "Recent") {
continue; continue;
}
cfg->SetPath(s); cfg->SetPath(s);
int poff = s.size(); int poff = s.size();

View File

@ -1428,7 +1428,7 @@ void log(const char* defaultMsg, ...)
wxGetApp().log.append(msg); wxGetApp().log.append(msg);
if (wxGetApp().IsMainLoopRunning()) { if (wxGetApp().IsMainLoopRunning()) {
LogDialog* d = wxGetApp().frame->logdlg; LogDialog* d = wxGetApp().frame->logdlg.get();
if (d && d->IsShown()) { if (d && d->IsShown()) {
d->Update(); d->Update();

View File

@ -544,12 +544,10 @@ void MainFrame::IOViewer()
baddialog(); \ baddialog(); \
cb->SetValidator(wxBoolIntValidator(&systemVerbose, val, val)); \ cb->SetValidator(wxBoolIntValidator(&systemVerbose, val, val)); \
} while (0) } while (0)
LogDialog::LogDialog() : keep_on_top_styler_(this) { LogDialog::LogDialog() :
dialogs::BaseDialog(nullptr, "Logging") {
const wxString dname = wxT("Logging"); const wxString dname = wxT("Logging");
if (!wxXmlResource::Get()->LoadDialog(this, wxGetApp().frame, dname))
baddialog();
SetEscapeId(wxID_OK); SetEscapeId(wxID_OK);
getlogf("SWI", VERBOSE_SWI); getlogf("SWI", VERBOSE_SWI);
getlogf("UnalignedMemory", VERBOSE_UNALIGNED_MEMORY); getlogf("UnalignedMemory", VERBOSE_UNALIGNED_MEMORY);

View File

@ -1,5 +1,5 @@
#ifndef VBAM_WX_DIALOGS_BASE_DIALOG_H_ #ifndef VBAM_WX_WIDGETS_KEEP_ON_TOP_STYLER_H_
#define VBAM_WX_DIALOGS_BASE_DIALOG_H_ #define VBAM_WX_WIDGETS_KEEP_ON_TOP_STYLER_H_
#include <wx/event.h> #include <wx/event.h>
@ -55,4 +55,4 @@ private:
} // namespace widgets } // namespace widgets
#endif // VBAM_WX_DIALOGS_BASE_DIALOG_H_ #endif // VBAM_WX_WIDGETS_KEEP_ON_TOP_STYLER_H_

16
src/wx/widgets/utils.cpp Normal file
View File

@ -0,0 +1,16 @@
#include "wx/widgets/utils.h"
#include <wx/display.h>
namespace widgets {
wxRect GetDisplayRect() {
wxRect display_rect;
for (unsigned int i = 0; i < wxDisplay::GetCount(); i++) {
display_rect.Union(wxDisplay(i).GetClientArea());
}
return display_rect;
}
} // namespace widgets

View File

@ -1,12 +1,18 @@
#ifndef VBAM_WX_DIALOGS_VALIDATED_CHILD_H_ #ifndef VBAM_WX_WIDGETS_UTILS_H_
#define VBAM_WX_DIALOGS_VALIDATED_CHILD_H_ #define VBAM_WX_WIDGETS_UTILS_H_
#include <cassert> #include <cassert>
#include <wx/string.h>
#include <wx/window.h> #include <wx/window.h>
#include <wx/gdicmn.h>
namespace dialogs { // This file contains a collection of various utility functions for wxWidgets.
namespace widgets {
// Helper function to get the display rectangle. Useful for avoiding drawing a
// dialog outside the screen.
wxRect GetDisplayRect();
// Helper functions to assert on the returned value. // Helper functions to assert on the returned value.
inline wxWindow* GetValidatedChild(const wxWindow* parent, inline wxWindow* GetValidatedChild(const wxWindow* parent,
@ -23,6 +29,7 @@ T* GetValidatedChild(const wxWindow* parent, const wxString& name) {
return child; return child;
} }
} // namespace dialogs
#endif // VBAM_WX_DIALOGS_VALIDATED_CHILD_H_ } // namespace widgets
#endif // VBAM_WX_WIDGETS_UTILS_H_

View File

@ -51,6 +51,7 @@
#include "wx/wayland.h" #include "wx/wayland.h"
#include "wx/widgets/group-check-box.h" #include "wx/widgets/group-check-box.h"
#include "wx/widgets/user-input-ctrl.h" #include "wx/widgets/user-input-ctrl.h"
#include "wx/widgets/utils.h"
#ifdef __WXGTK__ #ifdef __WXGTK__
#include <gdk/gdk.h> #include <gdk/gdk.h>
@ -567,14 +568,8 @@ bool wxvbamApp::OnInit() {
return false; return false;
} }
// Measure the full display area.
wxRect display_rect;
for (unsigned int i = 0; i < wxDisplay::GetCount(); i++) {
display_rect.Union(wxDisplay(i).GetClientArea());
}
// Ensure we are not drawing out of bounds. // Ensure we are not drawing out of bounds.
if (display_rect.Intersects(client_rect)) { if (widgets::GetDisplayRect().Intersects(client_rect)) {
frame->SetSize(client_rect); frame->SetSize(client_rect);
} }
@ -1258,11 +1253,6 @@ int MainFrame::ShowModal(wxDialog* dlg)
{ {
dlg->SetWindowStyle(dlg->GetWindowStyle() | wxCAPTION | wxRESIZE_BORDER); dlg->SetWindowStyle(dlg->GetWindowStyle() | wxCAPTION | wxRESIZE_BORDER);
if (OPTION(kDispKeepOnTop))
dlg->SetWindowStyle(dlg->GetWindowStyle() | wxSTAY_ON_TOP);
else
dlg->SetWindowStyle(dlg->GetWindowStyle() & ~wxSTAY_ON_TOP);
CheckPointer(dlg); CheckPointer(dlg);
StartModal(); StartModal();
int ret = dlg->ShowModal(); int ret = dlg->ShowModal();

View File

@ -14,6 +14,7 @@
#include "core/base/system.h" #include "core/base/system.h"
#include "wx/config/option-observer.h" #include "wx/config/option-observer.h"
#include "wx/config/option.h" #include "wx/config/option.h"
#include "wx/dialogs/base-dialog.h"
#include "wx/widgets/dpi-support.h" #include "wx/widgets/dpi-support.h"
#include "wx/widgets/keep-on-top-styler.h" #include "wx/widgets/keep-on-top-styler.h"
#include "wx/widgets/sdljoy.h" #include "wx/widgets/sdljoy.h"
@ -292,7 +293,7 @@ public:
// this won't actually be destroyed, but it needs to be tracked so only // this won't actually be destroyed, but it needs to be tracked so only
// one is ever up and it needs to be pinged when new messages arrive // one is ever up and it needs to be pinged when new messages arrive
LogDialog* logdlg; std::unique_ptr<LogDialog> logdlg;
// the cheat search dialog isn't destroyed or tracked, but it needs // the cheat search dialog isn't destroyed or tracked, but it needs
// to be cleared between games // to be cleared between games
@ -385,8 +386,6 @@ private:
void OnSize(wxSizeEvent& event); void OnSize(wxSizeEvent& event);
// Load a named wxDialog from the XRC file // Load a named wxDialog from the XRC file
wxDialog* LoadXRCDialog(const char* name); wxDialog* LoadXRCDialog(const char* name);
// Load a named wxDialog from the XRC file
wxDialog* LoadXRCropertySheetDialog(const char* name);
#include "wx/cmdhandlers.h" #include "wx/cmdhandlers.h"
}; };
@ -695,14 +694,13 @@ public:
DrawingPanel(wxWindow* parent, int _width, int _height); DrawingPanel(wxWindow* parent, int _width, int _height);
}; };
class LogDialog : public wxDialog { class LogDialog : public dialogs::BaseDialog {
public: public:
LogDialog(); LogDialog();
void Update(); void Update();
private: private:
wxTextCtrl* log; wxTextCtrl* log;
widgets::KeepOnTopStyler keep_on_top_styler_;
void Save(wxCommandEvent& ev); void Save(wxCommandEvent& ev);
void Clear(wxCommandEvent& ev); void Clear(wxCommandEvent& ev);