Ember
Loading...
Searching...
No Matches
OverrideSceneDialog.h
Go to the documentation of this file.
1#pragma once
2
4#include <cmath>
5#include <wx/button.h>
6#include <wx/checkbox.h>
7#include <wx/filename.h>
8#include <wx/sizer.h>
9#include <wx/statline.h>
10#include <wx/stattext.h>
11#include <wx/textctrl.h>
12#include <wx/timer.h>
13
14namespace EmberForge {
15
20 public:
21 enum { ID_PULSE_TIMER = wxID_HIGHEST + 1000 };
22
23 OverrideSceneDialog(wxWindow *parent, const wxString &xmlFileName)
24 : EmberUI::ScalableDialog(parent, wxID_ANY, "Override Current Scene", wxSize(500, 315), wxDEFAULT_DIALOG_STYLE),
25 m_pulseTimer(nullptr), m_pulsePhase(0.0f), m_normalBg(50, 50, 50), m_highlightBg(255, 250, 150) {
26 // Main sizer
27 wxBoxSizer *mainSizer = new wxBoxSizer(wxVERTICAL);
28
29 // Message
30 wxString message = wxString::Format("Load '%s' into the current scene?\n\n"
31 "This will replace the current behavior tree.",
32 xmlFileName);
33 wxStaticText *messageText = new wxStaticText(this, wxID_ANY, message);
34 mainSizer->Add(messageText, 0, wxALL | wxEXPAND, 15);
35
36 // Separator
37 mainSizer->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
38 mainSizer->AddSpacer(10);
39
40 // Rename checkbox
41 m_renameCheckbox = new wxCheckBox(this, wxID_ANY, "Rename scene to:");
42 m_renameCheckbox->SetValue(true); // Checked by default
43 mainSizer->Add(m_renameCheckbox, 0, wxLEFT | wxRIGHT, 15);
44 mainSizer->AddSpacer(8);
45
46 // Scene name input (pre-filled with XML filename without extension)
47 wxFileName fn(xmlFileName);
48 m_sceneNameInput = new wxTextCtrl(this, wxID_ANY, fn.GetName());
49 mainSizer->Add(m_sceneNameInput, 0, wxEXPAND | wxLEFT | wxRIGHT, 15);
50
51 mainSizer->AddSpacer(20);
52
53 // Add separator before buttons
54 mainSizer->Add(new wxStaticLine(this), 0, wxEXPAND | wxLEFT | wxRIGHT, 10);
55 mainSizer->AddSpacer(10);
56
57 // Standard dialog button sizer for proper button handling
58 wxStdDialogButtonSizer *buttonSizer = new wxStdDialogButtonSizer();
59
60 wxButton *okBtn = new wxButton(this, wxID_OK, "Load");
61 wxButton *cancelBtn = new wxButton(this, wxID_CANCEL, "Cancel");
62
63 buttonSizer->SetAffirmativeButton(okBtn);
64 buttonSizer->SetCancelButton(cancelBtn);
65 buttonSizer->Realize();
66
67 mainSizer->Add(buttonSizer, 0, wxALIGN_RIGHT | wxALL, 15);
68
69 SetSizer(mainSizer);
70
71 // Bind checkbox event to enable/disable text input
72 m_renameCheckbox->Bind(wxEVT_CHECKBOX,
73 [this](wxCommandEvent &evt) { m_sceneNameInput->Enable(m_renameCheckbox->GetValue()); });
74
75 // Bind text change event to update validation
76 m_sceneNameInput->Bind(wxEVT_TEXT, [this](wxCommandEvent &evt) { UpdateFieldColors(); });
77
78 // Set OK button as default
79 okBtn->SetDefault();
80
81 Centre();
82
83 // Start pulse animation timer for field validation (30ms = ~33 fps)
84 m_pulseTimer = new wxTimer(this, ID_PULSE_TIMER);
85 Connect(ID_PULSE_TIMER, wxEVT_TIMER, wxTimerEventHandler(OverrideSceneDialog::OnPulseTimer));
86 m_pulseTimer->Start(30);
87 }
88
90 if (m_pulseTimer) {
91 m_pulseTimer->Stop();
92 delete m_pulseTimer;
93 }
94 Disconnect(ID_PULSE_TIMER, wxEVT_TIMER, wxTimerEventHandler(OverrideSceneDialog::OnPulseTimer));
95 }
96
97 bool ShouldRename() const { return m_renameCheckbox->GetValue(); }
98 wxString GetSceneName() const { return m_sceneNameInput->GetValue(); }
99
100 private:
101 void OnPulseTimer(wxTimerEvent &event) {
102 m_pulsePhase += 0.02f; // Smooth increment (matches ParserConfigDialog)
103 if (m_pulsePhase > 1.0f) {
104 m_pulsePhase = 0.0f;
105 }
107 }
108
110 // Use sinusoidal easing for smooth pulse (matches ParserConfigDialog)
111 float t = std::sin(m_pulsePhase * 3.14159f * 2.0f) * 0.5f + 0.5f;
112
113 // Only flash if rename checkbox is checked AND field is empty
114 if (m_renameCheckbox->GetValue() && m_sceneNameInput->GetValue().Trim().IsEmpty()) {
115 wxColour pulseColor = InterpolateColor(m_normalBg, m_highlightBg, t);
116 m_sceneNameInput->SetBackgroundColour(pulseColor);
117 m_sceneNameInput->Refresh();
118 } else {
119 m_sceneNameInput->SetBackgroundColour(m_normalBg);
120 m_sceneNameInput->Refresh();
121 }
122 }
123
124 wxColour InterpolateColor(const wxColour &color1, const wxColour &color2, float t) {
125 if (t < 0.0f)
126 t = 0.0f;
127 if (t > 1.0f)
128 t = 1.0f;
129
130 int r = static_cast<int>(color1.Red() + (color2.Red() - color1.Red()) * t);
131 int g = static_cast<int>(color1.Green() + (color2.Green() - color1.Green()) * t);
132 int b = static_cast<int>(color1.Blue() + (color2.Blue() - color1.Blue()) * t);
133
134 return wxColour(r, g, b);
135 }
136
137 wxCheckBox *m_renameCheckbox;
138 wxTextCtrl *m_sceneNameInput;
139
140 // Pulse animation
141 wxTimer *m_pulseTimer;
143 wxColour m_normalBg;
145};
146
147} // namespace EmberForge
wxColour InterpolateColor(const wxColour &color1, const wxColour &color2, float t)
void OnPulseTimer(wxTimerEvent &event)
OverrideSceneDialog(wxWindow *parent, const wxString &xmlFileName)
DPI-aware dialog base class for scalable layouts.
ScalableDialog(wxWindow *parent, wxWindowID id, const wxString &title, const wxSize &logicalSize=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER)
Constructs the dialog with logical size for DPI scaling.
Definition Panel.h:8