Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
ConditionPresetLibraryPanel.cpp
Go to the documentation of this file.
1/**
2 * @file ConditionPresetLibraryPanel.cpp
3 * @brief Implementation of ConditionPresetLibraryPanel (Phase 24.1).
4 * @author Olympe Engine
5 * @date 2026-03-16
6 *
7 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
8 */
9
11
12#include <algorithm>
13#include "../../system/system_utils.h"
14
15// ImGui is only included when building the full editor (not in tests).
16// Tests exercise the logic methods directly without rendering.
17#ifndef OLYMPE_HEADLESS
18# include "../../third_party/imgui/imgui.h"
19#endif
20
21namespace Olympe {
22
23// ============================================================================
24// Constructor
25// ============================================================================
26
32
33// ============================================================================
34// Search filter
35// ============================================================================
36
41
42// ============================================================================
43// Selection
44// ============================================================================
45
47{
49}
50
51void ConditionPresetLibraryPanel::OnPresetSelected(const std::string& presetID)
52{
53 m_selectedPresetID = presetID;
54}
55
56// ============================================================================
57// Filtered presets
58// ============================================================================
59
64
65// ============================================================================
66// Reference analysis
67// ============================================================================
68
70 const std::map<std::string, std::vector<std::string>>& refMap)
71{
73}
74
75std::vector<std::string>
76ConditionPresetLibraryPanel::GetReferencingNodes(const std::string& presetID) const
77{
78 // Build reverse map: presetID -> list of nodeIDs that reference it
79 std::vector<std::string> nodes;
80 for (const auto& kv : m_refMap)
81 {
82 const std::string& nodeID = kv.first;
83 const std::vector<std::string>& presets = kv.second;
84
85 for (const auto& pid : presets)
86 {
87 if (pid == presetID)
88 {
89 nodes.push_back(nodeID);
90 break;
91 }
92 }
93 }
94 return nodes;
95}
96
97// ============================================================================
98// Action handlers
99// ============================================================================
100
102{
104 // Default: empty condition with default values
105 // Use simple naming without suffix or "New Condition..." prefix
106 newPreset.name = "Condition";
109 newPreset.right = Operand::CreateConst(0.0);
110
111 const std::string id = m_registry.CreatePreset(newPreset);
112
113 // Persist the new preset to disk
114 // Phase 24.2 — Use absolute path resolution to work in both IDE debug and built executable
115 m_registry.Save(ResolveResourcePath("Blueprints/Presets/condition_presets.json"));
116
117 if (OnPresetCreated)
118 {
119 OnPresetCreated(id);
120 }
121
123 return id;
124}
125
127 const std::string& presetID)
128{
129 const std::string newID = m_registry.DuplicatePreset(presetID);
130
131 // Persist the duplication to disk
132 // Phase 24.2 — Use absolute path resolution to work in both IDE debug and built executable
133 m_registry.Save(ResolveResourcePath("Blueprints/Presets/condition_presets.json"));
134
135 if (!newID.empty() && OnPresetCreated)
136 {
138 }
139
140 return newID;
141}
142
144{
145 m_presetToDelete = presetID;
147}
148
149void ConditionPresetLibraryPanel::OnDeleteConfirmed(const std::string& presetID)
150{
151 if (presetID != m_presetToDelete)
152 {
153 return;
154 }
155
156 m_registry.DeletePreset(presetID);
157
158 // Persist the deletion to disk
159 // Phase 24.2 — Use absolute path resolution to work in both IDE debug and built executable
160 m_registry.Save(ResolveResourcePath("Blueprints/Presets/condition_presets.json"));
161
162 if (m_selectedPresetID == presetID)
163 {
164 m_selectedPresetID.clear();
165 }
166
168 m_presetToDelete.clear();
169
170 if (OnPresetDeleted)
171 {
172 OnPresetDeleted(presetID);
173 }
174}
175
181
182// ============================================================================
183// Render (ImGui — skipped in headless builds / tests)
184// ============================================================================
185
187{
188#ifndef OLYMPE_HEADLESS
189 if (!m_isOpen) { return; }
190
191 ImGui::SetNextWindowSize(ImVec2(440, 500), ImGuiCond_FirstUseEver);
192 if (!ImGui::Begin("Condition Preset Library", &m_isOpen))
193 {
194 ImGui::End();
195 return;
196 }
197
199 ImGui::Separator();
201
203 {
205 }
206
207 ImGui::End();
208#endif
209}
210
211// ============================================================================
212// Private rendering helpers
213// ============================================================================
214
216{
217#ifndef OLYMPE_HEADLESS
218 // Phase 24.3 — Removed "+ Add Condition Preset" button as requested
219 // The button to add presets has been hidden from the UI
220
221 // Search box (left-aligned)
222 char searchBuf[256] = {};
223 if (m_searchFilter.size() < sizeof(searchBuf))
224 {
226 }
227 ImGui::SetNextItemWidth(160.0f);
228 if (ImGui::InputText("##Search", searchBuf, sizeof(searchBuf)))
229 {
231 }
232#endif
233}
234
236{
237#ifndef OLYMPE_HEADLESS
238 const std::vector<ConditionPreset> presets = GetFilteredPresets();
239
240 for (const auto& preset : presets)
241 {
243 }
244#endif
245}
246
247void ConditionPresetLibraryPanel::RenderPresetItem(const std::string& presetID,
248 const ConditionPreset& preset)
249{
250#ifndef OLYMPE_HEADLESS
251 const bool isSelected = (presetID == m_selectedPresetID);
252
253 ImGui::PushID(presetID.c_str());
254
255 // Collapsible header
257 if (isSelected) { flags |= ImGuiTreeNodeFlags_Selected; }
258
259 const std::string label = preset.name + " " + preset.GetPreview();
260 bool open = ImGui::CollapsingHeader(label.c_str(), flags);
261
262 if (ImGui::IsItemClicked())
263 {
264 OnPresetSelected(presetID);
265 }
266
267 // Action buttons on the same line
268 ImGui::SameLine();
269 if (ImGui::SmallButton("Dup"))
270 {
271 OnDuplicatePresetClicked(presetID);
272 }
273 ImGui::SameLine();
274 if (ImGui::SmallButton("X"))
275 {
276 OnDeletePresetClicked(presetID);
277 }
278
279 if (open)
280 {
281 ImGui::Indent();
282 ImGui::TextUnformatted(preset.GetPreview().c_str());
283 RenderReferenceAnalysis(presetID);
284 ImGui::Unindent();
285 }
286
287 ImGui::PopID();
288#endif
289}
290
292{
293#ifndef OLYMPE_HEADLESS
294 ImGui::OpenPopup("Delete Preset?");
295 if (ImGui::BeginPopupModal("Delete Preset?", nullptr,
297 {
299 if (preset)
300 {
301 ImGui::Text("Delete \"%s\"?", preset->name.c_str());
302 const std::vector<std::string> users = GetReferencingNodes(m_presetToDelete);
303 if (!users.empty())
304 {
305 ImGui::TextColored(ImVec4(1.f, 0.4f, 0.4f, 1.f),
306 "Warning: used by %d node(s)",
307 static_cast<int>(users.size()));
308 }
309 }
310
311 if (ImGui::Button("Delete"))
312 {
314 ImGui::CloseCurrentPopup();
315 }
316 ImGui::SameLine();
317 if (ImGui::Button("Cancel"))
318 {
320 ImGui::CloseCurrentPopup();
321 }
322
323 ImGui::EndPopup();
324 }
325#endif
326}
327
329{
330#ifndef OLYMPE_HEADLESS
331 const std::vector<std::string> users = GetReferencingNodes(presetID);
332 if (!users.empty())
333 {
334 std::string usedBy = "Used by: ";
335 for (size_t i = 0; i < users.size(); ++i)
336 {
337 if (i > 0) { usedBy += ", "; }
338 usedBy += users[i];
339 }
340 ImGui::TextDisabled("%s", usedBy.c_str());
341 }
342#endif
343}
344
345} // namespace Olympe
UI panel for managing Condition Presets globally (Phase 24.1).
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
void RenderReferenceAnalysis(const std::string &presetID)
std::vector< ConditionPreset > GetFilteredPresets() const
Returns filtered presets based on the current search filter.
void RenderPresetItem(const std::string &presetID, const ConditionPreset &preset)
std::string m_selectedPresetID
Highlighted preset ID.
std::string m_searchFilter
Current search text.
void SetSelectedPresetID(const std::string &id)
Selects the preset with the given ID.
std::string OnDuplicatePresetClicked(const std::string &presetID)
Handles the "Duplicate" button for a preset.
void OnPresetSelected(const std::string &presetID)
Selects a preset (highlights it in the list).
void OnDeleteCancelled()
Cancels the pending deletion and hides the confirmation dialog.
std::map< std::string, std::vector< std::string > > m_refMap
External reference map: nodeID -> list of preset IDs that node references.
ConditionPresetRegistry & m_registry
Global preset registry.
void SetSearchFilter(const std::string &filter)
Sets the search filter string (case-insensitive substring).
void SetReferenceMap(const std::map< std::string, std::vector< std::string > > &refMap)
Sets the reference analysis map (nodeID -> list of preset IDs used).
void OnDeletePresetClicked(const std::string &presetID)
Handles the "Delete" button for a preset.
ConditionPresetLibraryPanel(ConditionPresetRegistry &registry)
Constructs the panel with a reference to the preset registry.
void OnDeleteConfirmed(const std::string &presetID)
Confirms the pending deletion.
std::function< void(const std::string &)> OnPresetDeleted
Invoked after a preset is deleted.
std::string OnAddPresetClicked()
Handles the "Add Preset" button.
void Render()
Renders the full panel window using ImGui.
std::string m_presetToDelete
ID queued for deletion.
std::function< void(const std::string &)> OnPresetCreated
Invoked after a new preset is added.
std::vector< std::string > GetReferencingNodes(const std::string &presetID) const
Returns a list of node IDs that reference the given preset.
Manages the global pool of ConditionPreset objects.
void DeletePreset(const std::string &id)
Removes a preset from the registry.
std::string CreatePreset(const ConditionPreset &preset)
Adds a preset to the registry.
std::string DuplicatePreset(const std::string &id)
Creates an independent copy of an existing preset with a new UUID.
bool Save(const std::string &filepath) const
Saves all presets to a JSON file.
std::vector< ConditionPreset > GetFilteredPresets(const std::string &filter) const
Returns all presets whose name contains the filter substring.
ConditionPreset * GetPreset(const std::string &id)
Returns a mutable pointer to the preset, or nullptr if not found.
< Provides AssetID and INVALID_ASSET_ID
A globally-stored, reusable condition expression.
std::string name
Human-readable display name (e.g. "Condition #1")
static Operand CreateVariable(const std::string &variableID)
Factory — creates a Variable-mode operand.
Definition Operand.cpp:33
static Operand CreateConst(double constVal)
Factory — creates a Const-mode operand.
Definition Operand.cpp:43
std::string ResolveResourcePath(const std::string &relativePath)