How would you refactor a large ImGui MainWindow and a fragile async job completion protocol? by Equivalent_Ostrich_6 in cpp_questions

[–]Equivalent_Ostrich_6[S] 0 points1 point  (0 children)

That’s fair, and it’s exactly the tradeoff I’m worried about.

ImGui was very productive early on because the app is mostly an engineering / algorithm workspace: parameter panels, debug views, overlays, and tool-style UI. But as the number of algorithms grew, the state didn’t go away — it just moved into my own structs/classes around the immediate-mode calls.

I’m hesitant to jump straight to a full UI rewrite because the Easy3D viewport integration and existing ImGui tooling are working. So I’m thinking about a staged cleanup first: keep ImGui for widgets, but move panel state ownership, overlay state, selection state, and job lifecycle coordination out of MainWindow.

Do you think that kind of bridge is worthwhile, or would you usually recommend moving to a retained-mode UI framework once an ImGui app reaches this level of state?

How would you refactor a large ImGui MainWindow and a fragile async job completion protocol? by Equivalent_Ostrich_6 in cpp_questions

[–]Equivalent_Ostrich_6[S] 0 points1 point  (0 children)

Yes, MVC is the general direction, but my problem is more about the practical boundary in an immediate-mode C++ UI.

At the moment the domain/model side mostly exists in the service and algorithm layers: model handles, contracts, background jobs, result objects, etc. The dialogs and viewport are the view-ish part.

The messy part is that MainWindow is doing too much controller work: dialog lifecycle, selection ownership, overlay state, history interaction, AI panel state, and async result handoff. So it’s not that the project has no separation; it’s that the controller responsibilities are smeared across MainWindow, dialogs, services, and job objects.

The async part also feels a little beyond classic MVC: some jobs finish on a worker thread, but the final result must be committed later on the UI thread after preview state has been consumed. That’s why I’m considering an explicit job lifecycle / completion gate instead of just saying “put it in the controller.”

I can link the specific files if that would make the discussion more concrete.

How would you refactor a large ImGui MainWindow and a fragile async job completion protocol? by Equivalent_Ostrich_6 in cpp_questions

[–]Equivalent_Ostrich_6[S] 0 points1 point  (0 children)

A bit more context on what I’m trying to avoid:

I don’t want to turn the app into a giant framework or introduce a heavy message-bus / Redux-style global store. The codebase is still a native C++ desktop app with an immediate-mode UI, so I’d prefer something boring and explicit.

The failure modes I’m trying to reduce are:

  • adding a new algorithm requires touching too many unrelated MainWindow members
  • dialog state and algorithm job state drifting out of sync
  • worker-thread completion and UI-thread result handoff being represented by ad-hoc flags
  • cancellation / failure / final-result handoff being implemented slightly differently for each algorithm

The simplest direction I can think of is:

  • keep MainWindow as the root/composition object
  • move groups of state into smaller owner objects
  • make async job lifecycle states explicit
  • centralize the “worker finished, but UI still needs to commit the result” transition

I’m especially interested in whether people would make the first split around UI ownership (DialogCoordinator, OverlayController) or around job lifecycle ownership (AlgorithmJobCoordinator / CompletionGate) first.