But if the form is already show on screen, then it will be gotten from
there.
You are right, and I missed it. GetForm scans Screen.CustomForms before it ever
looks at the creator list, so returning nil from the creator only suppresses a
window that does not already exist. Thank you for catching it.
Worse than it first looks, and worth stating plainly: the LCL adds a form to
Screen in the constructor and removes it in the destructor, and that scan has no
visibility test. So hiding is not enough either — Close leaves the form on the
list, GetForm hands it back, and Apply/ShowForm puts it on screen again. The
provider has to destroy its window, not close it. "Only close not available" has
to mean free.
With that, I went looking for whether the "before the desktop switch" timing you
described is actually available. Two of the three cases already have it.
Starting debug: already ordered correctly
TDebugManager.OnDebuggerChangeState calls the state notification list first, and
only afterwards UpdateButtonsAndMenuItems and UpdateToolStatus. UpdateToolStatus
is what maps the state to itDebugger, which sets MainIDE.ToolStatus, which calls
EnableDebugDesktop, which calls UseDesktop and then RestoreDesktop.
So a debugger state-change subscriber runs before the desktop switch, in the same
call. That is the hook you were already planning to add for state changes, and it
lands in the right place with no extra work: the provider frees its window on
dsInit, and the restore that follows finds nothing on screen. It is also where the
existing console does its Clear, so the precedent is there.
IDE startup: also already there
TMainIDE.RestoreIDEWindows calls the lihtIDERestoreWindows handlers and then
EnvironmentGuiOpts.Desktop.RestoreDesktop. Since that is a single pass after the
packages have registered, a provider hooking
LazarusIDE.AddHandlerOnIDERestoreWindows can reconcile once, before anything is
restored, with no dependency on load order. That was the case you called crucial,
and I think it is covered.
Manual desktop switch: the one gap
All switches funnel through TEnvGuiOptions.UseDesktop — the desktop manager for a
manual pick, and EnableDebugDesktop / DisableDebugDesktop for the debug ones. One
choke point, which is the good news.
The bad news is that nothing there announces itself in advance except
EnvironmentOptions.DoBeforeWrite on the first line. That is hookable through
AddHandlerBeforeWrite in ideoptionsintf, but it is the wrong signal — the IDE
options dialog and the package editor call DoBeforeWrite too, so a provider
hooking it would tear its window down whenever anyone wrote options.
What would close the gap is a handler list on UseDesktop, fired before
Desktop.RestoreDesktop. That is additive and touches no layout storage and no
placement logic, so I do not think it runs into the maintenance problem you
raised — but it is IDE code and therefore your call, not mine. If you would rather
not, the manual-switch case degrades to the same "user opens it once from the
menu" that the Visible flag already gives us.
One thing off your list, incidentally: I can find no path where loading a project
switches desktops. The only callers of UseDesktop are the desktop manager and the
two debug ones.
Now the questions you actually asked
1) Should picking a run-params mode from the dropdown immediately close and open
windows?
No, and for your reason: it is easy to pick the wrong one and pick again, and
windows appearing and disappearing under that is unpleasant. I would define the
stored selection as "what the next debug session will use", and let it sit until a
trigger.
2) Should a desktop change update the selection?
Yes, and I would make it unconditional rather than reasoning per case. Your three
sub-cases — desktop closes the window, desktop opens it from closed, window open
and stays open — each have a defensible answer, but a rule that depends on what
the desktop happens to contain is hard for a user to predict and harder to test.
Uniform is better: on any desktop switch, reconcile first (free anything not
available), then let the restore open whatever the desktop asks for. You said
simplest, and I think simplest is also the most explicable.
Starting debug obviously triggers it, and per the above it already can.
So the whole rule is: selection changes are stored but not acted on; reconcile
happens at IDE startup, at any desktop switch, and at dsInit. Three triggers, all
of them "something is about to lay out windows anyway".