Modernize Brush in Atlas

Use dynamic event bindings allowing to bind all events in all sort of
ways. Addionally no need for subclassing wx widgets.

Signed-off-by: Ralph Sennhauser <ralph.sennhauser@gmail.com>
This commit is contained in:
Ralph Sennhauser 2026-07-08 19:57:44 +02:00
parent 7ee476928b
commit 32b9713781
No known key found for this signature in database
5 changed files with 64 additions and 108 deletions

View file

@ -44,7 +44,7 @@ static Brush* g_Brush_CurrentlyActive = NULL; // only one brush can be active at
const float Brush::STRENGTH_MULTIPLIER = 1024.f; const float Brush::STRENGTH_MULTIPLIER = 1024.f;
Brush::Brush() Brush::Brush()
: m_Shape(CIRCLE), m_Size(4), m_Strength(1.f), m_IsActive(false) : m_Shape(Shape::CIRCLE), m_Size(4), m_Strength(1.f), m_IsActive(false)
{ {
} }
@ -76,9 +76,9 @@ int Brush::GetWidth() const
{ {
switch (m_Shape) switch (m_Shape)
{ {
case CIRCLE: case Shape::CIRCLE:
return m_Size; return m_Size;
case SQUARE: case Shape::SQUARE:
return m_Size; return m_Size;
default: default:
wxFAIL; wxFAIL;
@ -108,7 +108,7 @@ std::vector<float> Brush::GetData() const
switch (m_Shape) switch (m_Shape)
{ {
case CIRCLE: case Shape::CIRCLE:
{ {
int i = 0; int i = 0;
// All calculations are done in units of half-tiles, since that // All calculations are done in units of half-tiles, since that
@ -131,7 +131,7 @@ std::vector<float> Brush::GetData() const
break; break;
} }
case SQUARE: case Shape::SQUARE:
{ {
int i = 0; int i = 0;
for (int y = 0; y < height; ++y) for (int y = 0; y < height; ++y)
@ -144,6 +144,17 @@ std::vector<float> Brush::GetData() const
return data; return data;
} }
int Brush::GetSize() const
{
return m_Size;
}
void Brush::SetSize(int size)
{
m_Size = size;
Send();
}
float Brush::GetStrength() const float Brush::GetStrength() const
{ {
return m_Strength; return m_Strength;
@ -152,115 +163,54 @@ float Brush::GetStrength() const
void Brush::SetStrength(float strength) void Brush::SetStrength(float strength)
{ {
m_Strength = strength; m_Strength = strength;
Send();
} }
void Brush::SetCircle(int size) void Brush::SetShape(Shape shape)
{ {
m_Shape = CIRCLE; m_Shape = shape;
m_Size = size; Send();
} }
void Brush::SetSquare(int size)
{
m_Shape = SQUARE;
m_Size = size;
}
//////////////////////////////////////////////////////////////////////////
class BrushShapeCtrl : public wxRadioBox
{
public:
BrushShapeCtrl(wxWindow* parent, wxArrayString& shapes, Brush& brush)
: wxRadioBox(parent, wxID_ANY, _("Shape"), wxDefaultPosition, wxDefaultSize, shapes, 0, wxRA_SPECIFY_ROWS),
m_Brush(brush)
{
SetSelection(m_Brush.m_Shape);
}
private:
Brush& m_Brush;
void OnChange(wxCommandEvent& WXUNUSED(evt))
{
m_Brush.m_Shape = (Brush::BrushShape)GetSelection();
m_Brush.Send();
}
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(BrushShapeCtrl, wxRadioBox)
EVT_RADIOBOX(wxID_ANY, BrushShapeCtrl::OnChange)
END_EVENT_TABLE()
class BrushSizeCtrl: public wxSpinCtrl
{
public:
BrushSizeCtrl(wxWindow* parent, Brush& brush)
: wxSpinCtrl(parent, wxID_ANY, wxString::Format(_T("%d"), brush.m_Size), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, brush.m_Size),
m_Brush(brush)
{
}
private:
Brush& m_Brush;
void OnChange(wxSpinEvent& WXUNUSED(evt))
{
m_Brush.m_Size = GetValue();
m_Brush.Send();
}
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(BrushSizeCtrl, wxSpinCtrl)
EVT_SPINCTRL(wxID_ANY, BrushSizeCtrl::OnChange)
END_EVENT_TABLE()
class BrushStrengthCtrl : public wxSpinCtrl
{
public:
BrushStrengthCtrl(wxWindow* parent, Brush& brush)
: wxSpinCtrl(parent, wxID_ANY, wxString::Format(_T("%d"), (int)(10.f*brush.m_Strength)), wxDefaultPosition, wxDefaultSize, wxSP_ARROW_KEYS, 0, 100, (int)(10.f*brush.m_Strength)),
m_Brush(brush)
{
}
private:
Brush& m_Brush;
void OnChange(wxSpinEvent& WXUNUSED(evt))
{
m_Brush.m_Strength = GetValue()/10.f;
m_Brush.Send();
}
DECLARE_EVENT_TABLE();
};
BEGIN_EVENT_TABLE(BrushStrengthCtrl, wxSpinCtrl)
EVT_SPINCTRL(wxID_ANY, BrushStrengthCtrl::OnChange)
END_EVENT_TABLE()
void Brush::CreateUI(wxWindow* parent, wxSizer* sizer) void Brush::CreateUI(wxWindow* parent, wxSizer* sizer)
{ {
wxArrayString shapes; // Must match order of BrushShape enum // Must match order of Brush::Shape enum
wxArrayString shapes;
shapes.Add(_("Circle")); shapes.Add(_("Circle"));
shapes.Add(_("Square")); shapes.Add(_("Square"));
// TODO (maybe): get rid of the extra static box, by not using wxRadioBox // TODO (maybe): get rid of the extra static box, by not using wxRadioBox
sizer->Add(new BrushShapeCtrl(parent, shapes, *this), wxSizerFlags().Expand().Border(wxALL, 5)); auto* brushShapeCtrl = new wxRadioBox(parent, wxID_ANY, _("Shape"), wxDefaultPosition, wxDefaultSize, shapes, 0, wxRA_SPECIFY_ROWS);
brushShapeCtrl->Bind(wxEVT_RADIOBOX, [this, brushShapeCtrl](auto&){ SetShape(static_cast<Shape>(brushShapeCtrl->GetSelection())); });
sizer->Add(brushShapeCtrl, wxSizerFlags().Expand().Border(wxALL, 5));
sizer->AddSpacer(5); sizer->AddSpacer(5);
// TODO: These are yucky auto* brushSizeLabel = new wxStaticText(parent, wxID_ANY, _("Size"));
auto* brushSizeCtrl = new wxSpinCtrl(parent, wxID_ANY,
wxString::Format(_T("%d"), GetSize()),
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER,
0, 100, GetSize());
auto burshSizeSetter = [this, brushSizeCtrl](auto&){ SetSize(brushSizeCtrl->GetValue()); };
brushSizeCtrl->Bind(wxEVT_SPINCTRL, burshSizeSetter);
auto* brushStrengthLabel = new wxStaticText(parent, wxID_ANY, _("Strength"));
auto* brushStrengthCtrl = new wxSpinCtrl(parent, wxID_ANY,
wxString::Format(_T("%d"), static_cast<int>(10.f * GetStrength())),
wxDefaultPosition, wxDefaultSize,
wxSP_ARROW_KEYS | wxTE_PROCESS_ENTER,
0, 100, static_cast<int>(10.f * GetStrength()));
auto burshStrenghtSetter = [this, brushStrengthCtrl](auto&){ SetStrength(brushStrengthCtrl->GetValue() / 10.f); };
brushStrengthCtrl->Bind(wxEVT_SPINCTRL, burshStrenghtSetter);
wxFlexGridSizer* spinnerSizer = new wxFlexGridSizer(2, 5, 5); wxFlexGridSizer* spinnerSizer = new wxFlexGridSizer(2, 5, 5);
spinnerSizer->AddGrowableCol(1); spinnerSizer->AddGrowableCol(1);
spinnerSizer->Add(new wxStaticText(parent, wxID_ANY, _("Size")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT));
spinnerSizer->Add(new BrushSizeCtrl(parent, *this), wxSizerFlags().Expand()); spinnerSizer->Add(brushSizeLabel, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT));
spinnerSizer->Add(new wxStaticText(parent, wxID_ANY, _("Strength")), wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT)); spinnerSizer->Add(brushSizeCtrl, wxSizerFlags().Expand());
spinnerSizer->Add(new BrushStrengthCtrl(parent, *this), wxSizerFlags().Expand()); spinnerSizer->Add(brushStrengthLabel, wxSizerFlags().Align(wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT));
spinnerSizer->Add(brushStrengthCtrl, wxSizerFlags().Expand());
sizer->Add(spinnerSizer, wxSizerFlags().Expand().Border(wxALL, 5)); sizer->Add(spinnerSizer, wxSizerFlags().Expand().Border(wxALL, 5));
} }

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2025 Wildfire Games. /* Copyright (C) 2026 Wildfire Games.
* This file is part of 0 A.D. * This file is part of 0 A.D.
* *
* 0 A.D. is free software: you can redistribute it and/or modify * 0 A.D. is free software: you can redistribute it and/or modify
@ -29,6 +29,8 @@ class Brush
friend class BrushSizeCtrl; friend class BrushSizeCtrl;
friend class BrushStrengthCtrl; friend class BrushStrengthCtrl;
public: public:
enum class Shape { CIRCLE = 0, SQUARE};
Brush(); Brush();
~Brush(); ~Brush();
@ -38,8 +40,10 @@ public:
int GetHeight() const; int GetHeight() const;
std::vector<float> GetData() const; std::vector<float> GetData() const;
void SetCircle(int size); void SetShape(Shape shape);
void SetSquare(int size);
int GetSize() const;
void SetSize(int size);
float GetStrength() const; float GetStrength() const;
void SetStrength(float strength); void SetStrength(float strength);
@ -54,8 +58,7 @@ private:
// If active, send SetBrush message to the game // If active, send SetBrush message to the game
void Send(); void Send();
enum BrushShape { CIRCLE = 0, SQUARE}; Shape m_Shape;
BrushShape m_Shape;
int m_Size; int m_Size;
float m_Strength; float m_Strength;
bool m_IsActive; bool m_IsActive;

View file

@ -43,7 +43,8 @@ class FillTerrain : public StateDrivenTool<FillTerrain>
public: public:
FillTerrain() FillTerrain()
{ {
m_Brush.SetSquare(2); m_Brush.SetShape(Brush::Shape::SQUARE);
m_Brush.SetSize(2);
SetState(&Waiting); SetState(&Waiting);
} }

View file

@ -53,7 +53,8 @@ public:
{ {
SetState(&Waiting); SetState(&Waiting);
m_EyedropperBrush.SetSquare(2); m_EyedropperBrush.SetShape(Brush::Shape::SQUARE);
m_EyedropperBrush.SetSize(2);
} }

View file

@ -43,7 +43,8 @@ class ReplaceTerrain : public StateDrivenTool<ReplaceTerrain>
public: public:
ReplaceTerrain() ReplaceTerrain()
{ {
m_Brush.SetSquare(2); m_Brush.SetShape(Brush::Shape::SQUARE);
m_Brush.SetSize(2);
SetState(&Waiting); SetState(&Waiting);
} }