Fix Enter key behaviour on Windows

Commit fe192fe58f made it possible for
events to properly propagate to the top window. As on Windows Enter is
also used for navigation, i.e. used to trigger the dialogs default action,
which in our case seems to be quit.

Tell the wxSpinCtrl to fire wxEVT_TEXT_ENTER instead and handle it.

The wxCommandEvent wxEVT_TEXT_ENTER doesn't carry the value, so need to
use dynamic event binding (which is preferred way anyway).

Also show the numPlayerSpinner as a spinner instead of a text field to
match visuals to actual behaviour.

Fixes: #9026
Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
This commit is contained in:
Ralph Sennhauser
2026-07-05 08:27:18 +02:00
parent 32b9713781
commit 3c8b7dfa7a
2 changed files with 43 additions and 14 deletions
@@ -192,35 +192,35 @@ public:
wxFlexGridSizer* gridSizer = new wxFlexGridSizer(3, 5, 5);
gridSizer->AddGrowableCol(2);
wxSpinCtrl* foodCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerFood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
wxSpinCtrl* foodCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerFood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(resourceSizer->GetStaticBox(), ID_DefaultFood, foodCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(resourceSizer->GetStaticBox(), wxID_ANY, _("Food")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(foodCtrl,
_("Initial value of food resource")), wxSizerFlags().Expand());
m_Controls.food = foodCtrl;
wxSpinCtrl* woodCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerWood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
wxSpinCtrl* woodCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerWood, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(resourceSizer->GetStaticBox(), ID_DefaultWood, woodCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(resourceSizer->GetStaticBox(), wxID_ANY, _("Wood")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(woodCtrl,
_("Initial value of wood resource")), wxSizerFlags().Expand());
m_Controls.wood = woodCtrl;
wxSpinCtrl* metalCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerMetal, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
wxSpinCtrl* metalCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerMetal, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(resourceSizer->GetStaticBox(), ID_DefaultMetal, metalCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(resourceSizer->GetStaticBox(), wxID_ANY, _("Metal")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(metalCtrl,
_("Initial value of metal resource")), wxSizerFlags().Expand());
m_Controls.metal = metalCtrl;
wxSpinCtrl* stoneCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerStone, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
wxSpinCtrl* stoneCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerStone, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(resourceSizer->GetStaticBox(), ID_DefaultStone, stoneCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(resourceSizer->GetStaticBox(), wxID_ANY, _("Stone")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(stoneCtrl,
_("Initial value of stone resource")), wxSizerFlags().Expand());
m_Controls.stone = stoneCtrl;
wxSpinCtrl* popCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerPop, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, INT_MAX);
wxSpinCtrl* popCtrl = new wxSpinCtrl(resourceSizer->GetStaticBox(), ID_PlayerPop, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER, 0, INT_MAX);
gridSizer->Add(new DefaultCheckbox(resourceSizer->GetStaticBox(), ID_DefaultPop, popCtrl), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL));
gridSizer->Add(new wxStaticText(resourceSizer->GetStaticBox(), wxID_ANY, _("Pop limit")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT));
gridSizer->Add(Tooltipped(popCtrl,
@@ -477,6 +477,14 @@ private:
}
}
void OnEditSpin(wxCommandEvent&)
{
if (!m_InGUIUpdate)
{
SendToEngine();
}
}
void OnPlayerColor(wxCommandEvent& WXUNUSED(evt))
{
if (!m_InGUIUpdate)
@@ -493,22 +501,22 @@ private:
// and we don't want to handle the same event twice
}
void OnNumPlayersSpin(wxSpinEvent& evt)
void SetNumPlayers(int newNumPlayers)
{
if (!m_InGUIUpdate)
{
wxASSERT(evt.GetInt() > 0);
wxASSERT(newNumPlayers > 0);
// When wxMessageBox pops up, wxSpinCtrl loses focus, which
// forces another EVT_SPINCTRL event, which we don't want
// to handle, so we check here for a change
if (evt.GetInt() == (int)m_NumPlayers)
if (newNumPlayers == (int)m_NumPlayers)
{
return; // No change
}
size_t oldNumPlayers = m_NumPlayers;
m_NumPlayers = evt.GetInt();
m_NumPlayers = newNumPlayers;
if (m_NumPlayers < oldNumPlayers)
{
@@ -574,14 +582,17 @@ BEGIN_EVENT_TABLE(PlayerSettingsControl, wxPanel)
EVT_BUTTON(ID_CameraClear, PlayerSettingsControl::OnEdit)
EVT_CHECKBOX(wxID_ANY, PlayerSettingsControl::OnEdit)
EVT_CHOICE(wxID_ANY, PlayerSettingsControl::OnEdit)
EVT_TEXT(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersText)
EVT_TEXT(wxID_ANY, PlayerSettingsControl::OnEdit)
EVT_SPINCTRL(ID_NumPlayers, PlayerSettingsControl::OnNumPlayersSpin)
EVT_SPINCTRL(ID_PlayerFood, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerWood, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerMetal, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerStone, PlayerSettingsControl::OnEditSpin)
EVT_SPINCTRL(ID_PlayerPop, PlayerSettingsControl::OnEditSpin)
EVT_TEXT_ENTER(ID_PlayerFood, PlayerSettingsControl::OnEditSpin)
EVT_TEXT_ENTER(ID_PlayerWood, PlayerSettingsControl::OnEditSpin)
EVT_TEXT_ENTER(ID_PlayerMetal, PlayerSettingsControl::OnEditSpin)
EVT_TEXT_ENTER(ID_PlayerStone, PlayerSettingsControl::OnEditSpin)
EVT_TEXT_ENTER(ID_PlayerPop, PlayerSettingsControl::OnEditSpin)
END_EVENT_TABLE();
PlayerSettingsControl::PlayerSettingsControl(wxWindow* parent, ScenarioEditor& scenarioEditor)
@@ -604,9 +615,19 @@ PlayerSettingsControl::PlayerSettingsControl(wxWindow* parent, ScenarioEditor& s
boxSizer->AddSpacer(10);
wxSpinCtrl* numPlayersSpin = new wxSpinCtrl(topBox, ID_NumPlayers, wxEmptyString, wxDefaultPosition, wxSize(40, -1));
numPlayersSpin->SetValue(MAX_NUM_PLAYERS);
numPlayersSpin->SetRange(1, MAX_NUM_PLAYERS);
// NOTE: on MSW pressing Enter with wxSpinCtrl is used to navigate, ie
// it triggers the default Window action which is ok / close. So we
// have to explicitly generate the wxCommandEvent wxEVT_TEXT_ENTER and
// capture it.
// https://gitea.wildfiregames.com/0ad/0ad/issues/9026
wxSpinCtrl* numPlayersSpin = new wxSpinCtrl(topBox, ID_NumPlayers,
wxEmptyString, wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER,
1, MAX_NUM_PLAYERS, MAX_NUM_PLAYERS);
auto numPlayerSetter = [this, numPlayersSpin](auto&){ SetNumPlayers(numPlayersSpin->GetValue()); };
numPlayersSpin->Bind(wxEVT_SPINCTRL, numPlayerSetter);
numPlayersSpin->Bind(wxEVT_TEXT_ENTER, numPlayerSetter);
boxSizer->Add(numPlayersSpin);
gridSizer->Add(boxSizer);
@@ -34,6 +34,7 @@
#include <wx/spinctrl.h>
#include <wx/stattext.h>
#include <wx/string.h>
#include <wx/textctrl.h>
#include <wx/toolbar.h>
#include <wx/translation.h>
@@ -186,6 +187,11 @@ void Brush::CreateUI(wxWindow* parent, wxSizer* sizer)
sizer->AddSpacer(5);
// NOTE: on MSW pressing Enter with wxSpinCtrl is used to navigate, ie it
// triggers the default Window action which is ok / close. So we have to
// explicitly generate the wxCommandEvent wxEVT_TEXT_ENTER and capture it.
// https://gitea.wildfiregames.com/0ad/0ad/issues/9026
auto* brushSizeLabel = new wxStaticText(parent, wxID_ANY, _("Size"));
auto* brushSizeCtrl = new wxSpinCtrl(parent, wxID_ANY,
wxString::Format(_T("%d"), GetSize()),
@@ -194,6 +200,7 @@ void Brush::CreateUI(wxWindow* parent, wxSizer* sizer)
0, 100, GetSize());
auto burshSizeSetter = [this, brushSizeCtrl](auto&){ SetSize(brushSizeCtrl->GetValue()); };
brushSizeCtrl->Bind(wxEVT_SPINCTRL, burshSizeSetter);
brushSizeCtrl->Bind(wxEVT_TEXT_ENTER, burshSizeSetter);
auto* brushStrengthLabel = new wxStaticText(parent, wxID_ANY, _("Strength"));
auto* brushStrengthCtrl = new wxSpinCtrl(parent, wxID_ANY,
@@ -203,6 +210,7 @@ void Brush::CreateUI(wxWindow* parent, wxSizer* sizer)
0, 100, static_cast<int>(10.f * GetStrength()));
auto burshStrenghtSetter = [this, brushStrengthCtrl](auto&){ SetStrength(brushStrengthCtrl->GetValue() / 10.f); };
brushStrengthCtrl->Bind(wxEVT_SPINCTRL, burshStrenghtSetter);
brushStrengthCtrl->Bind(wxEVT_TEXT_ENTER, burshStrenghtSetter);
wxFlexGridSizer* spinnerSizer = new wxFlexGridSizer(2, 5, 5);
spinnerSizer->AddGrowableCol(1);