Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
NodeConditionsEditModal.cpp
Go to the documentation of this file.
1/**
2 * @file NodeConditionsEditModal.cpp
3 * @brief Implementation of NodeConditionsEditModal (Phase 24-REFONTE).
4 * @author Olympe Engine
5 * @date 2026-03-17
6 *
7 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
8 */
9
11
12#include <algorithm>
13#include <sstream>
14
15// ImGui is only compiled in the full editor build.
16#ifndef OLYMPE_HEADLESS
17# include "../../third_party/imgui/imgui.h"
18#endif
19
20namespace Olympe {
21
22// ============================================================================
23// Constructor
24// ============================================================================
25
30
31// ============================================================================
32// Visibility / lifecycle
33// ============================================================================
34
35void NodeConditionsEditModal::Open(const std::vector<NodeConditionRef>& currentRefs,
36 const std::vector<ConditionRef>& operandRefs)
37{
41 m_isOpen = true;
42 m_isConfirmed = false;
43 m_pickerOpen = false;
44 m_dropdownFilter.clear();
45}
46
48{
49 m_isOpen = false;
50 m_pickerOpen = false;
51}
52
53// ============================================================================
54// Result
55// ============================================================================
56
57const std::vector<NodeConditionRef>& NodeConditionsEditModal::GetConditionRefs() const
58{
59 return m_workingCopy;
60}
61
62const std::vector<ConditionRef>& NodeConditionsEditModal::GetConditionOperandRefs() const
63{
65}
66
67// ============================================================================
68// Condition management (testable without ImGui)
69// ============================================================================
70
72{
73 return m_workingCopy.size();
74}
75
76void NodeConditionsEditModal::AddCondition(const std::string& presetID)
77{
78 if (presetID.empty())
79 return;
80
82 m_workingCopy.emplace_back(presetID, op);
84}
85
87{
88 if (index >= m_workingCopy.size())
89 return;
90
91 m_workingCopy.erase(m_workingCopy.begin() + static_cast<int>(index));
93}
94
96{
97 if (index >= m_workingCopy.size())
98 return;
99
100 m_workingCopy[index].logicalOp = (index == 0) ? LogicalOp::Start : op;
101}
102
104{
105 if (index == 0 || index >= m_workingCopy.size())
106 return;
107
108 std::swap(m_workingCopy[index - 1], m_workingCopy[index]);
110}
111
113{
114 if (index + 1 >= m_workingCopy.size())
115 return;
116
117 std::swap(m_workingCopy[index], m_workingCopy[index + 1]);
119}
120
122{
123 m_isConfirmed = true;
124 if (OnApply)
125 OnApply();
126 Close();
127}
128
129// ============================================================================
130// Dropdown helper
131// ============================================================================
132
137
138std::vector<ConditionPreset>
140{
141 std::vector<ConditionPreset> result;
142
143 if (m_dropdownFilter.empty())
144 {
145 for (const auto& id : m_registry.GetAllPresetIDs())
146 {
148 if (p)
149 result.push_back(*p);
150 }
151 }
152 else
153 {
154 for (const auto& id : m_registry.FindPresetsByName(m_dropdownFilter))
155 {
157 if (p)
158 result.push_back(*p);
159 }
160 }
161
162 return result;
163}
164
165// ============================================================================
166// Rendering
167// ============================================================================
168
170{
171#ifndef OLYMPE_HEADLESS
172 if (!m_isOpen)
173 return;
174
175 ImGui::OpenPopup("Edit Conditions##NodeConditionsEditModal");
176
177 ImGui::SetNextWindowSize(ImVec2(600.f, 400.f), ImGuiCond_FirstUseEver);
178 if (ImGui::BeginPopupModal("Edit Conditions##NodeConditionsEditModal",
179 nullptr, ImGuiWindowFlags_None))
180 {
181 // Blue title
182 ImGui::TextColored(ImVec4(0.0f, 0.8f, 1.0f, 1.0f), "Edit Conditions");
183 ImGui::Separator();
184
185 ImGui::Text("Conditions:");
186 ImGui::BeginChild("ConditionsList", ImVec2(0.f, 200.f), true);
187 for (size_t i = 0; i < m_workingCopy.size(); ++i)
188 {
190 }
191 ImGui::EndChild();
192
193 ImGui::Spacing();
195
196 ImGui::Separator();
198
199 ImGui::EndPopup();
200 }
201#endif
202}
203
205 const NodeConditionRef& ref)
206{
207#ifndef OLYMPE_HEADLESS
208 ImGui::PushID(static_cast<int>(index));
209
210 // Logical operator dropdown (disabled for first entry)
211 if (index == 0)
212 {
213 ImGui::TextDisabled(" ");
214 }
215 else
216 {
217 const char* ops[] = { "And", "Or" };
218 int current = (ref.logicalOp == LogicalOp::And) ? 0 : 1;
219 ImGui::SetNextItemWidth(60.f);
220 if (ImGui::Combo("##op", &current, ops, 2))
222 }
223
224 ImGui::SameLine();
225
226 // Condition preset preview — green text
227 const ConditionPreset* preset = m_registry.GetPreset(ref.presetID);
228 if (preset)
229 ImGui::TextColored(ImVec4(0.0f, 1.0f, 0.0f, 1.0f),
230 "[%s] %s", preset->name.c_str(), preset->GetPreview().c_str());
231 else
232 ImGui::TextColored(ImVec4(1.f, 0.3f, 0.3f, 1.f),
233 "(missing: %s)", ref.presetID.c_str());
234
235 ImGui::SameLine();
236
237 // Move up
238 if (ImGui::SmallButton("^"))
240
241 ImGui::SameLine();
242
243 // Move down
244 if (ImGui::SmallButton("v"))
246
247 ImGui::SameLine();
248
249 // Delete
250 if (ImGui::SmallButton("X"))
252
253 ImGui::PopID();
254#endif
255}
256
258{
259#ifndef OLYMPE_HEADLESS
260 if (ImGui::Button("+ Add Condition"))
262
263 if (m_pickerOpen)
264 {
265 static char filterBuf[256] = {};
266 if (ImGui::InputText("##filter", filterBuf, sizeof(filterBuf)))
267 SetDropdownFilter(std::string(filterBuf));
268
269 const std::vector<ConditionPreset> presets = GetFilteredPresetsForDropdown();
270 for (const auto& preset : presets)
271 {
272 const std::string label = preset.name + " " + preset.GetPreview();
273 if (ImGui::Selectable(label.c_str()))
274 {
276 m_pickerOpen = false;
277 filterBuf[0] = '\0';
279 }
280 }
281
282 if (presets.empty())
283 ImGui::TextDisabled("No presets found.");
284 }
285#endif
286}
287
289{
290#ifndef OLYMPE_HEADLESS
291 if (ImGui::Button("Apply", ImVec2(150.f, 24.f)))
292 Confirm();
293
294 ImGui::SameLine();
295
296 if (ImGui::Button("Cancel", ImVec2(150.f, 24.f)))
297 Close();
298#endif
299}
300
301// ============================================================================
302// Internal helpers
303// ============================================================================
304
310
311} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Dedicated modal dialog for editing a NodeBranch's condition list (Phase 24-REFONTE).
Manages the global pool of ConditionPreset objects.
std::vector< std::string > GetAllPresetIDs() const
Returns all preset UUIDs in display order.
std::vector< std::string > FindPresetsByName(const std::string &substring) const
Returns UUIDs of all presets whose name contains a substring.
ConditionPreset * GetPreset(const std::string &id)
Returns a mutable pointer to the preset, or nullptr if not found.
std::string m_dropdownFilter
Preset-picker search filter.
std::vector< ConditionRef > m_workingCopyOperandRefs
Phase 24: Operand data.
bool m_pickerOpen
Whether the add-preset picker is open.
std::function< void()> OnApply
Optional callback fired when the user clicks "Apply" (Confirm).
void AddCondition(const std::string &presetID)
Appends a new condition for the given preset to the working copy.
void RemoveCondition(size_t index)
Removes the condition at the given index from the working copy.
void RenderConditionRow(size_t index, const NodeConditionRef &ref)
std::vector< NodeConditionRef > m_workingCopy
Editable in-progress copy.
const std::vector< NodeConditionRef > & GetConditionRefs() const
Returns the edited condition ref list.
size_t GetConditionCount() const
Returns the number of conditions in the working copy.
void NormalizeLogicalOps()
Ensures the first condition's logicalOp is always Start.
const std::vector< ConditionRef > & GetConditionOperandRefs() const
Returns the edited operand ref list (Phase 24).
void Render()
Renders the modal dialog using ImGui.
void Confirm()
Programmatically confirms the modal (equivalent to clicking Apply).
void SetDropdownFilter(const std::string &filter)
Sets the search filter for the "Add Condition" preset picker.
std::vector< ConditionPreset > GetFilteredPresetsForDropdown() const
Returns presets matching the current dropdown filter.
void MoveConditionUp(size_t index)
Moves the condition at index one position earlier (swap with index-1).
void Open(const std::vector< NodeConditionRef > &currentRefs, const std::vector< ConditionRef > &operandRefs=std::vector< ConditionRef >())
Opens the modal, loading the given condition refs for editing.
void MoveConditionDown(size_t index)
Moves the condition at index one position later (swap with index+1).
NodeConditionsEditModal(ConditionPresetRegistry &registry)
Constructs the modal bound to a global preset registry.
ConditionPresetRegistry & m_registry
Shared global registry.
void Close()
Closes the modal without confirming changes.
void SetLogicalOp(size_t index, LogicalOp op)
Sets the logical operator for the condition at the given index.
< Provides AssetID and INVALID_ASSET_ID
LogicalOp
How this condition is combined with the one preceding it.
@ Or
Combined with OR.
@ Start
First condition in the list (no logical combinator)
@ And
Combined with AND.
A globally-stored, reusable condition expression.
One entry in a NodeBranch's conditions list.