@@ -33,9 +33,6 @@ public static class ImGuiExtensionManager
3333 private static object ? nodesContext ;
3434 private static object ? plotContext ;
3535
36- // Track whether the ImGui context has been shared with extensions
37- private static bool imGuiContextSet ;
38-
3936 // Extensions whose assembly has been probed, so each is loaded and reflected over at most once
4037 private static ImGuiAppExtensions probed ;
4138
@@ -58,8 +55,102 @@ public static class ImGuiExtensionManager
5855 public static void Initialize ( ImGuiAppExtensions extensions )
5956 {
6057 Enabled = extensions & ImGuiAppExtensions . All ;
58+ Probe ( Enabled ) ;
59+ }
60+
61+ /// <summary>
62+ /// Turns on the given extensions in addition to those already enabled, after launch. Unlike
63+ /// <see cref="Initialize(ImGuiAppExtensions)"/>, which replaces the enabled set, this only adds
64+ /// to it, so an extension that is already on keeps its context and its state.
65+ /// </summary>
66+ /// <remarks>
67+ /// <para>
68+ /// Each newly enabled extension is set up exactly as it is at launch: its assembly is probed if
69+ /// it has not been, it is handed the ImGui context when <see cref="SetImGuiContext(ImGuiContextPtr?)"/>
70+ /// has already run, and ImNodes and ImPlot are given a context of their own with the dark
71+ /// style. ImGuizmo, which needs a <c>BeginFrame</c> every frame, gets one at once when this is
72+ /// called inside a frame, so it can be drawn with in the same frame; <see cref="BeginFrame"/>
73+ /// covers it from the next frame on.
74+ /// </para>
75+ /// <para>
76+ /// Call it on the render thread, between frames or inside <c>OnRender</c> before the extension
77+ /// is drawn with. Enabling an extension that is already on does nothing.
78+ /// </para>
79+ /// </remarks>
80+ /// <param name="extensions">The extensions to turn on.</param>
81+ /// <returns>
82+ /// Which of <paramref name="extensions"/> are ready to draw with: ImGuizmo when its assembly was
83+ /// found, ImNodes and ImPlot when their context exists. Before the ImGui context has been shared,
84+ /// no context can exist yet, so ImNodes and ImPlot are reported when their assembly was found.
85+ /// An extension asked for and missing from the result is not referenced, and drawing with it
86+ /// faults in native code.
87+ /// </returns>
88+ public static ImGuiAppExtensions Enable ( ImGuiAppExtensions extensions )
89+ {
90+ ImGuiAppExtensions requested = extensions & ImGuiAppExtensions . All ;
91+ ImGuiAppExtensions added = requested & ~ Enabled ;
92+
93+ if ( added != ImGuiAppExtensions . None )
94+ {
95+ Enabled |= added ;
96+ Probe ( added ) ;
97+
98+ // Before the context is shared, launch will set these up along with everything else.
99+ if ( IsImGuiContextShared )
100+ {
101+ ImGuiContextPtr context = ImGui . GetCurrentContext ( ) ;
102+ SetImGuiContextFor ( added , context ) ;
103+ CreateExtensionContexts ( ) ;
104+
105+ // ImGuizmo draws into a window its BeginFrame opens; inside a frame that has already
106+ // begun, BeginFrame has passed it by, so it is begun here instead.
107+ if ( added . HasFlag ( ImGuiAppExtensions . ImGuizmo ) && context . WithinFrameScope )
108+ {
109+ BeginFrameFor ( ImGuiAppExtensions . ImGuizmo ) ;
110+ }
111+ }
112+ }
113+
114+ return Ready ( requested ) ;
115+ }
116+
117+ /// <summary>
118+ /// Gets which of the given extensions are ready to draw with. See <see cref="Enable(ImGuiAppExtensions)"/>.
119+ /// </summary>
120+ private static ImGuiAppExtensions Ready ( ImGuiAppExtensions extensions )
121+ {
122+ ImGuiAppExtensions ready = ImGuiAppExtensions . None ;
123+
124+ if ( IsImGuizmoAvailable )
125+ {
126+ ready |= ImGuiAppExtensions . ImGuizmo ;
127+ }
128+
129+ if ( IsImGuiContextShared ? IsImNodesContextCreated : IsImNodesAvailable )
130+ {
131+ ready |= ImGuiAppExtensions . ImNodes ;
132+ }
133+
134+ if ( IsImGuiContextShared ? IsImPlotContextCreated : IsImPlotAvailable )
135+ {
136+ ready |= ImGuiAppExtensions . ImPlot ;
137+ }
138+
139+ return ready & extensions ;
140+ }
61141
62- ImGuiAppExtensions toProbe = Enabled & ~ probed ;
142+ /// <summary>
143+ /// Gets whether <see cref="SetImGuiContext(ImGuiContextPtr?)"/> has run since the last
144+ /// <see cref="Cleanup"/>, which is what a launched application or a started harness has done.
145+ /// </summary>
146+ internal static bool IsImGuiContextShared { get ; private set ; }
147+
148+ /// <summary>
149+ /// Loads and reflects over each of the given extensions that has not been probed already.
150+ /// </summary>
151+ private static void Probe ( ImGuiAppExtensions extensions )
152+ {
153+ ImGuiAppExtensions toProbe = extensions & ~ probed ;
63154 probed |= toProbe ;
64155
65156 if ( toProbe . HasFlag ( ImGuiAppExtensions . ImGuizmo ) )
@@ -213,15 +304,25 @@ private static void InitializeImPlot()
213304 public static void BeginFrame ( )
214305 {
215306 // Guard: extension BeginFrame calls require a valid ImGui context
216- if ( ! imGuiContextSet )
307+ if ( ! IsImGuiContextShared )
217308 {
218309 return ;
219310 }
220311
312+ BeginFrameFor ( Enabled ) ;
313+ }
314+
315+ /// <summary>
316+ /// Calls BeginFrame for each of the given extensions that is enabled and has one.
317+ /// </summary>
318+ private static void BeginFrameFor ( ImGuiAppExtensions extensions )
319+ {
320+ ImGuiAppExtensions toBegin = extensions & Enabled ;
321+
221322 // Call ImGuizmo.BeginFrame() if available
222323 try
223324 {
224- if ( IsEnabled ( ImGuiAppExtensions . ImGuizmo ) )
325+ if ( toBegin . HasFlag ( ImGuiAppExtensions . ImGuizmo ) )
225326 {
226327 imGuizmoBeginFrame ? . Invoke ( null , null ) ;
227328 }
@@ -234,7 +335,7 @@ public static void BeginFrame()
234335 // Call ImNodes.BeginFrame() if available
235336 try
236337 {
237- if ( IsEnabled ( ImGuiAppExtensions . ImNodes ) )
338+ if ( toBegin . HasFlag ( ImGuiAppExtensions . ImNodes ) )
238339 {
239340 imNodesBeginFrame ? . Invoke ( null , null ) ;
240341 }
@@ -247,7 +348,7 @@ public static void BeginFrame()
247348 // Call ImPlot.BeginFrame() if available
248349 try
249350 {
250- if ( IsEnabled ( ImGuiAppExtensions . ImPlot ) )
351+ if ( toBegin . HasFlag ( ImGuiAppExtensions . ImPlot ) )
251352 {
252353 imPlotBeginFrame ? . Invoke ( null , null ) ;
253354 }
@@ -265,12 +366,21 @@ public static void BeginFrame()
265366 public static void SetImGuiContext ( ImGuiContextPtr ? context = null )
266367 {
267368 // Use current context if none provided
268- ImGuiContextPtr contextToSet = context ?? ImGui . GetCurrentContext ( ) ;
369+ SetImGuiContextFor ( Enabled , context ?? ImGui . GetCurrentContext ( ) ) ;
370+ IsImGuiContextShared = true ;
371+ }
372+
373+ /// <summary>
374+ /// Hands the ImGui context to each of the given extensions that is enabled.
375+ /// </summary>
376+ private static void SetImGuiContextFor ( ImGuiAppExtensions extensions , ImGuiContextPtr contextToSet )
377+ {
378+ ImGuiAppExtensions toSet = extensions & Enabled ;
269379
270380 // Set context for ImGuizmo if available
271381 try
272382 {
273- if ( IsEnabled ( ImGuiAppExtensions . ImGuizmo ) )
383+ if ( toSet . HasFlag ( ImGuiAppExtensions . ImGuizmo ) )
274384 {
275385 imGuizmoSetImGuiContext ? . Invoke ( null , [ contextToSet ] ) ;
276386 }
@@ -283,7 +393,7 @@ public static void SetImGuiContext(ImGuiContextPtr? context = null)
283393 // Set context for ImNodes if available
284394 try
285395 {
286- if ( IsEnabled ( ImGuiAppExtensions . ImNodes ) )
396+ if ( toSet . HasFlag ( ImGuiAppExtensions . ImNodes ) )
287397 {
288398 imNodesSetImGuiContext ? . Invoke ( null , [ contextToSet ] ) ;
289399 }
@@ -296,7 +406,7 @@ public static void SetImGuiContext(ImGuiContextPtr? context = null)
296406 // Set context for ImPlot if available
297407 try
298408 {
299- if ( IsEnabled ( ImGuiAppExtensions . ImPlot ) )
409+ if ( toSet . HasFlag ( ImGuiAppExtensions . ImPlot ) )
300410 {
301411 imPlotSetImGuiContext ? . Invoke ( null , [ contextToSet ] ) ;
302412 }
@@ -305,8 +415,6 @@ public static void SetImGuiContext(ImGuiContextPtr? context = null)
305415 {
306416 DebugLogger . Log ( $ "ImGuiExtensionManager: Error calling ImPlot.SetImGuiContext(): { ex . InnerException ? . Message ?? ex . Message } ") ;
307417 }
308-
309- imGuiContextSet = true ;
310418 }
311419
312420 /// <summary>
@@ -424,7 +532,7 @@ public static void Cleanup()
424532 }
425533 }
426534
427- imGuiContextSet = false ;
535+ IsImGuiContextShared = false ;
428536 }
429537
430538 /// <summary>
0 commit comments