Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
NodeConditionsPanel.h
Go to the documentation of this file.
1/**
2 * @file NodeConditionsPanel.h
3 * @brief UI Properties panel for a NodeBranch – 4-section layout (Phase 24-REFONTE).
4 * @author Olympe Engine
5 * @date 2026-03-17
6 *
7 * @details
8 * NodeConditionsPanel renders the Properties panel for a selected NodeBranch.
9 * It is divided into four read-only / static sections, with actual condition
10 * editing delegated to NodeConditionsEditModal:
11 *
12 * @code
13 * ╔══════════════════════════════════════════════════════════╗
14 * ║ Section 1 — Title bar (blue background) ║
15 * ║ Is Health Critical? Name ║
16 * ╠══════════════════════════════════════════════════════════╣
17 * ║ Section 2 — Exec pins (static, never editable) ║
18 * ║ >> In Then >> ║
19 * ║ Else >> ║
20 * ╠══════════════════════════════════════════════════════════╣
21 * ║ Section 3 — Conditions preview (READ-ONLY, green text) ║
22 * ║ [mHealth] <= [2] ║
23 * ║ And [mSpeed] <= [100.00] ║
24 * ║ Or [mSpeed] == [Pin : 1] ║
25 * ║ [ Edit Conditions ] ║
26 * ╠══════════════════════════════════════════════════════════╣
27 * ║ Section 4 — Dynamic data pins (yellow, only when used) ║
28 * ║ * In #1: [mSpeed] == [Pin : 1] ║
29 * ╚══════════════════════════════════════════════════════════╝
30 * @endcode
31 *
32 * Editing is opened via the "Edit Conditions" button which sets
33 * m_editModalOpen = true; the caller renders NodeConditionsEditModal separately.
34 *
35 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
36 */
37
38#pragma once
39
40#include <string>
41#include <vector>
42#include <functional>
43
44#include "../ConditionPreset/ConditionPreset.h"
45#include "../ConditionPreset/ConditionPresetRegistry.h"
46#include "../ConditionPreset/NodeConditionRef.h"
47#include "../ConditionPreset/DynamicDataPin.h"
48#include "../Modals/NodeConditionsEditModal.h"
49#include "../../BlueprintEditor/ConditionRef.h"
50#include "../../TaskSystem/LocalBlackboard.h"
51
52namespace Olympe {
53
54/**
55 * @class NodeConditionsPanel
56 * @brief ImGui sub-panel for managing the condition list of a single NodeBranch.
57 *
58 * @details
59 * The panel does NOT own the registry — it holds a reference.
60 * The caller is responsible for persisting changes from GetConditionRefs()
61 * back to the node definition.
62 *
63 * Dependency injection:
64 * NodeConditionsPanel panel(registry);
65 * panel.SetConditionRefs(node.conditions);
66 * panel.SetDynamicPins(node.dynamicPins); // optional read-only display
67 * panel.Render();
68 * if (panel.IsDirty()) {
69 * node.conditions = panel.GetConditionRefs();
70 * panel.ClearDirty();
71 * }
72 */
74public:
75
76 /**
77 * @brief Constructs the panel bound to a global preset registry.
78 * @param registry Global ConditionPresetRegistry (must outlive this panel).
79 */
81
83
84 // Non-copyable
87
88 // -----------------------------------------------------------------------
89 // State accessors
90 // -----------------------------------------------------------------------
91
92 /**
93 * @brief Replaces the panel's internal condition list.
94 *
95 * Typically called when a new node is selected in the editor.
96 *
97 * @param refs Condition references from node.conditions.
98 */
99 void SetConditionRefs(const std::vector<NodeConditionRef>& refs);
100
101 /**
102 * @brief Returns the current (possibly modified) condition list.
103 */
104 const std::vector<NodeConditionRef>& GetConditionRefs() const;
105
106 /**
107 * @brief Replaces the panel's inline operand list (parallel to conditionRefs).
108 *
109 * Each entry corresponds 1:1 with a NodeConditionRef and stores the
110 * editable operand data (mode, variable name, const value, dynamicPinID).
111 * Typically called alongside SetConditionRefs() when a node is selected.
112 *
113 * @param refs Inline operand refs from node.conditionOperandRefs.
114 */
115 void SetConditionOperandRefs(const std::vector<ConditionRef>& refs);
116
117 /**
118 * @brief Returns the current inline operand list.
119 *
120 * The host should read this after IsDirty() returns true and persist it
121 * to node.conditionOperandRefs.
122 */
123 const std::vector<ConditionRef>& GetConditionOperandRefs() const;
124
125 /**
126 * @brief Provides the read-only list of dynamic pins for display.
127 *
128 * The panel shows these pins in a read-only section. Ownership stays
129 * with the DynamicDataPinManager; the panel only renders them.
130 *
131 * @param pins Current dynamic pins associated with this node.
132 */
133 void SetDynamicPins(const std::vector<DynamicDataPin>& pins);
134
135 /**
136 * @brief Returns true if the condition list has been modified since the
137 * last call to ClearDirty().
138 */
139 bool IsDirty() const { return m_dirty; }
140
141 /**
142 * @brief Resets the dirty flag.
143 */
144 void ClearDirty() { m_dirty = false; }
145
146 // -----------------------------------------------------------------------
147 // Node name (for title section)
148 // -----------------------------------------------------------------------
149
150 /**
151 * @brief Sets the node name displayed in the title section.
152 * @param name Display name (e.g. "Is Health Critical?").
153 */
154 void SetNodeName(const std::string& name) { m_nodeName = name; }
155
156 /**
157 * @brief Returns the current node name shown in the title bar.
158 */
159 const std::string& GetNodeName() const { return m_nodeName; }
160
161 // -----------------------------------------------------------------------
162 // Edit-modal interaction
163 // -----------------------------------------------------------------------
164
165 /**
166 * @brief Returns true if the "Edit Conditions" button was clicked this frame.
167 *
168 * The caller should open NodeConditionsEditModal when this is true, then
169 * call ClearEditModalRequest() to reset the flag.
170 */
172
173 /**
174 * @brief Resets the edit-modal request flag.
175 */
177
178 // -----------------------------------------------------------------------
179 // Rendering (ImGui — guarded by OLYMPE_HEADLESS in tests)
180 // -----------------------------------------------------------------------
181
182 /**
183 * @brief Renders the condition list and dynamic-pin display using ImGui.
184 *
185 * Must be called once per frame inside an ImGui window context.
186 * Internally wrapped in `#ifndef OLYMPE_HEADLESS` so tests can call
187 * the logic methods without linking ImGui.
188 */
189 void Render();
190
191 /**
192 * @brief Renders all conditions inline as an editable list.
193 *
194 * Each condition is displayed as a row containing:
195 * - Condition preview (green text, from ConditionPreset::GetPreview)
196 * - Logical-operator dropdown ("And" / "Or") — skipped for the first row
197 * - Delete button ("X") that removes the condition immediately
198 *
199 * Below the list a "[+ Add Condition]" button opens a popup that lets
200 * the user pick a preset from the registry (filtered by name).
201 *
202 * Modifications set the dirty flag so the host can sync via GetConditionRefs().
203 *
204 * Layout:
205 * @code
206 * ┌─ "Structured Conditions (evaluated with implicit AND)" ──┐
207 * │ [mHealth] <= [2] [And▼] [X] │
208 * │ [mSpeed] <= [100.00] [Or▼] [X] │
209 * │ [+ Add Condition] │
210 * └──────────────────────────────────────────────────────────┘
211 * @endcode
212 *
213 * Guarded by `#ifndef OLYMPE_HEADLESS` — no-op in headless test builds.
214 */
215 void RenderConditionList();
216
217 /**
218 * @brief Phase 24 UX Refactor: Renders condition editor inline (no modal popup).
219 *
220 * Replaces the modal-based editing with an inline collapsible section that
221 * allows users to add, edit, reorder, and delete conditions directly in the
222 * properties panel without opening a separate modal window.
223 *
224 * Guarded by `#ifndef OLYMPE_HEADLESS`.
225 */
227
228 /**
229 * @brief Renders a single condition row with edit/move/delete controls.
230 *
231 * @param index Index of the condition in m_conditionRefs
232 * @param ref Reference to the NodeConditionRef being rendered
233 */
235
236 /**
237 * @brief Renders the "Add Condition" button and preset picker inline.
238 *
239 * When clicked, shows a filterable list of available presets from the registry.
240 */
242
243 // -----------------------------------------------------------------------
244 // Callbacks
245 // -----------------------------------------------------------------------
246
247 /** Fired when a preset is selected or changed. Arg: preset ID. */
248 std::function<void(const std::string&)> OnPresetChanged;
249
250 /**
251 * @brief Fired after the edit modal is confirmed and condition refs are
252 * updated, signalling that dynamic pins must be regenerated.
253 *
254 * The host should connect this to DynamicDataPinManager::RegeneratePinsFromConditions()
255 * or NodeBranchRenderer::TriggerPinRegeneration() so that the canvas
256 * updates immediately on the next render frame.
257 *
258 * Example:
259 * @code
260 * panel.OnDynamicPinsNeedRegeneration = [&]() {
261 * pinManager.RegeneratePinsFromConditions(node.conditionRefs);
262 * node.dynamicPins = pinManager.GetAllPins();
263 * };
264 * @endcode
265 */
267
268 /**
269 * @brief Should be called by the host when a preset is deleted from the
270 * registry. Removes any NodeConditionRef pointing to that preset.
271 *
272 * @param deletedPresetID UUID of the preset that was removed.
273 */
274 void OnPresetDeleted(const std::string& deletedPresetID);
275
276 /**
277 * @brief Sets the list of local variables from EntityBlackboard (Phase 24).
278 *
279 * Used to populate variable dropdowns in the operand selector with both
280 * local and global variables.
281 *
282 * @param localVars Vector of local variable definitions from the entity blackboard.
283 */
284 void SetLocalVariables(const std::vector<BlackboardEntry>& localVars)
285 {
287 }
288
289 // -----------------------------------------------------------------------
290 // Condition management (also testable without ImGui)
291 // -----------------------------------------------------------------------
292
293 /**
294 * @brief Appends a new condition reference with the given preset.
295 *
296 * The logicalOp is automatically set to LogicalOp::And (except for
297 * the first entry, which receives LogicalOp::Start).
298 *
299 * @param presetID UUID of the ConditionPreset to reference.
300 */
301 void AddCondition(const std::string& presetID);
302
303 /**
304 * @brief Removes the condition at the given index.
305 * @param index Zero-based index in the condition list.
306 */
307 void RemoveCondition(size_t index);
308
309 /**
310 * @brief Changes the logical operator for the condition at the given index.
311 *
312 * Index 0 is always forced to LogicalOp::Start regardless of the value
313 * passed in.
314 *
315 * @param index Zero-based index.
316 * @param op New LogicalOp (And or Or; Start is ignored for index > 0).
317 */
318 void SetLogicalOp(size_t index, LogicalOp op);
319
320 /**
321 * @brief Returns the number of conditions currently in the list.
322 */
323 size_t GetConditionCount() const;
324
325 // -----------------------------------------------------------------------
326 // Validation
327 // -----------------------------------------------------------------------
328
329 /**
330 * @brief Returns true when every condition ref points to an existing preset.
331 */
332 bool IsValid() const;
333
334 /**
335 * @brief Returns a list of error strings describing validation failures.
336 *
337 * Empty if IsValid() returns true.
338 */
339 std::vector<std::string> Validate() const;
340
341 // -----------------------------------------------------------------------
342 // Search helper (used by the dropdown)
343 // -----------------------------------------------------------------------
344
345 /**
346 * @brief Sets the search/filter string used by the "Add Condition" dropdown.
347 * @param filter Substring filter (empty = show all).
348 */
349 void SetDropdownFilter(const std::string& filter);
350
351 /**
352 * @brief Returns the current dropdown filter.
353 */
354 const std::string& GetDropdownFilter() const { return m_dropdownFilter; }
355
356 /**
357 * @brief Returns presets matching the current dropdown filter.
358 *
359 * Delegates to ConditionPresetRegistry::FindPresetsByName when the filter
360 * is non-empty; returns all presets otherwise.
361 */
362 std::vector<ConditionPreset> GetFilteredPresetsForDropdown() const;
363
364private:
365
366 // -----------------------------------------------------------------------
367 // ImGui rendering helpers — 4-section layout
368 // -----------------------------------------------------------------------
369
370 /** Section 1: title bar (blue background, node name). */
371 void RenderTitleSection();
372
373 /** Section 2: static exec pins (In / Then / Else, never editable). */
375
376 /** Section 3: read-only conditions preview (green text) + "Edit Conditions" button. */
378
379 /** Section 4: dynamic data pins (yellow, only when non-empty). */
381
382 /**
383 * @brief Renders the operand mode selector + value field for one operand.
384 *
385 * Inline widget used inside RenderConditionList() for each condition row.
386 * Renders a three-button radio group (Variable | Const | Pin) followed by
387 * a mode-specific input:
388 * - Variable: text field for the blackboard key (e.g. "mHealth")
389 * - Const: numeric text field (e.g. "100.0")
390 * - Pin: read-only label "Pin-in #N" (auto-assigned)
391 *
392 * @param cref ConditionRef to modify (mode and value fields).
393 * @param isLeft True for the left operand, false for the right operand.
394 * @param pinLabel Short "Pin-in #N" label for Pin-mode display (may be empty).
395 */
397 const std::string& pinLabel = "");
398
399 /**
400 * @brief Renders the operator combo box (==, !=, <, <=, >, >=).
401 *
402 * Inline widget used inside RenderConditionList() for each condition row.
403 * Updates `cref.operatorStr` on selection.
404 *
405 * @param cref ConditionRef to modify.
406 */
408
409 // -----------------------------------------------------------------------
410 // Internal helpers
411 // -----------------------------------------------------------------------
412
413 /** Fixes up the first condition's logicalOp to always be Start. */
414 void NormalizeLogicalOps();
415
416 // -----------------------------------------------------------------------
417 // State
418 // -----------------------------------------------------------------------
419
420 ConditionPresetRegistry& m_registry; ///< Shared global registry
421 std::vector<NodeConditionRef> m_conditionRefs; ///< Current node's conditions
422 std::vector<ConditionRef> m_conditionOperandRefs; ///< Inline operand data (parallel to m_conditionRefs)
423 std::vector<DynamicDataPin> m_dynamicPins; ///< Read-only dynamic pins for display
424 std::vector<BlackboardEntry> m_localVariables; ///< Local variables from entity (Phase 24)
425 std::string m_nodeName; ///< Node display name for title section
426 bool m_dirty = false; ///< Modification flag
427 bool m_editModalRequested = false; ///< Set when Edit button is clicked
428 bool m_pickerOpen = false; ///< Phase 24: Inline preset picker visibility
429
430 NodeConditionsEditModal m_editModal; ///< Owned modal for condition editing
431
432 std::string m_dropdownFilter; ///< Filter text for "Add Condition" dropdown
433 bool m_dropdownOpen = false; ///< Whether the add-dropdown is shown (legacy, kept for API compat)
434};
435
436} // namespace Olympe
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Manages the global pool of ConditionPreset objects.
Modal dialog for adding, removing, and reordering conditions on a node.
ImGui sub-panel for managing the condition list of a single NodeBranch.
void RenderOperatorDropdown(ConditionRef &cref)
Renders the operator combo box (==, !=, <, <=, >, >=).
std::string m_nodeName
Node display name for title section.
std::vector< NodeConditionRef > m_conditionRefs
Current node's conditions.
void RenderTitleSection()
Section 1: title bar (blue background, node name).
const std::vector< ConditionRef > & GetConditionOperandRefs() const
Returns the current inline operand list.
void SetConditionRefs(const std::vector< NodeConditionRef > &refs)
Replaces the panel's internal condition list.
void RemoveCondition(size_t index)
Removes the condition at the given index.
void ClearDirty()
Resets the dirty flag.
void RenderInlineConditionRow(size_t index, const NodeConditionRef &ref)
Renders a single condition row with edit/move/delete controls.
const std::string & GetDropdownFilter() const
Returns the current dropdown filter.
void SetNodeName(const std::string &name)
Sets the node name displayed in the title section.
void RenderConditionsPreview()
Section 3: read-only conditions preview (green text) + "Edit Conditions" button.
NodeConditionsEditModal m_editModal
Owned modal for condition editing.
std::vector< DynamicDataPin > m_dynamicPins
Read-only dynamic pins for display.
void NormalizeLogicalOps()
Fixes up the first condition's logicalOp to always be Start.
void RenderConditionList()
Renders all conditions inline as an editable list.
const std::vector< NodeConditionRef > & GetConditionRefs() const
Returns the current (possibly modified) condition list.
ConditionPresetRegistry & m_registry
Shared global registry.
bool m_dropdownOpen
Whether the add-dropdown is shown (legacy, kept for API compat)
bool IsEditModalRequested() const
Returns true if the "Edit Conditions" button was clicked this frame.
void RenderInlineConditionEditor()
Phase 24 UX Refactor: Renders condition editor inline (no modal popup).
void AddCondition(const std::string &presetID)
Appends a new condition reference with the given preset.
void RenderExecPinsSection()
Section 2: static exec pins (In / Then / Else, never editable).
std::vector< ConditionRef > m_conditionOperandRefs
Inline operand data (parallel to m_conditionRefs)
std::vector< std::string > Validate() const
Returns a list of error strings describing validation failures.
void RenderDynamicPinsSection()
Section 4: dynamic data pins (yellow, only when non-empty).
void SetConditionOperandRefs(const std::vector< ConditionRef > &refs)
Replaces the panel's inline operand list (parallel to conditionRefs).
bool IsValid() const
Returns true when every condition ref points to an existing preset.
void OnPresetDeleted(const std::string &deletedPresetID)
Should be called by the host when a preset is deleted from the registry.
bool m_dirty
Modification flag.
std::string m_dropdownFilter
Filter text for "Add Condition" dropdown.
NodeConditionsPanel & operator=(const NodeConditionsPanel &)=delete
bool m_editModalRequested
Set when Edit button is clicked.
std::function< void()> OnDynamicPinsNeedRegeneration
Fired after the edit modal is confirmed and condition refs are updated, signalling that dynamic pins ...
void SetDynamicPins(const std::vector< DynamicDataPin > &pins)
Provides the read-only list of dynamic pins for display.
const std::string & GetNodeName() const
Returns the current node name shown in the title bar.
void Render()
Renders the condition list and dynamic-pin display using ImGui.
std::vector< BlackboardEntry > m_localVariables
Local variables from entity (Phase 24)
void SetLocalVariables(const std::vector< BlackboardEntry > &localVars)
Sets the list of local variables from EntityBlackboard (Phase 24).
std::vector< ConditionPreset > GetFilteredPresetsForDropdown() const
Returns presets matching the current dropdown filter.
void SetLogicalOp(size_t index, LogicalOp op)
Changes the logical operator for the condition at the given index.
std::function< void(const std::string &)> OnPresetChanged
Fired when a preset is selected or changed.
NodeConditionsPanel(const NodeConditionsPanel &)=delete
void ClearEditModalRequest()
Resets the edit-modal request flag.
void RenderInlineAddCondition()
Renders the "Add Condition" button and preset picker inline.
size_t GetConditionCount() const
Returns the number of conditions currently in the list.
bool IsDirty() const
Returns true if the condition list has been modified since the last call to ClearDirty().
void SetDropdownFilter(const std::string &filter)
Sets the search/filter string used by the "Add Condition" dropdown.
bool m_pickerOpen
Phase 24: Inline preset picker visibility.
void RenderOperandDropdown(ConditionRef &cref, bool isLeft, const std::string &pinLabel="")
Renders the operand mode selector + value field for one operand.
< Provides AssetID and INVALID_ASSET_ID
LogicalOp
How this condition is combined with the one preceding it.
Stores the complete reference for one condition including operand-to-DynamicDataPin mapping.
One entry in a NodeBranch's conditions list.