Steps:
Create a new application, add the docking site, add 2 or more panels to the docking site, let's say Panel1, Panel2 and Panel3
Create the OnCloseQuery event for Panel1. Assign the same method (Panel1CloseQuery) to the OnCloseQuery event of all panels.
so you have
Panel1.OnCloseQuery = Panel1CloseQuery.
Panel2.OnCloseQuery = Panel1CloseQuery.
Panel3.OnCloseQuery = Panel1CloseQuery.
Make this the code of the method:
procedure TForm1.Panel1CloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if sender is TLMDDockPanel then
begin
outputdebugstring(pchar('Sender: '+TLMDDockPanel(sender).Name));
CanClose := false; // << this is just so you can repeat the test as much as you want
end;
end;
Run the application.
Undock panel1 and leave it floating on the desktop. Undock panel2 and panel3 and dock both into Panel1.
Hit the 'x' close button on any panel.
The Sender will always be Panel1, for any of the 'x' buttons you press.
I expected that hitting the 'x' of Panel2 would call the event with the sender = Panel2, and the same for Panel3.
Is that something that can be fixed?
UPDATE:
This is way worse than I first thought.
When you attempt to close a docked panel, the OnCloseQuery event happens to ALL panels in that site, even for the not visible ones!
And unless all the handlers set CanClose to true, it won't close. And when it closes, it closes ALL the panels!
This makes absolutely no sense to me, can you please review how this works?
Comments
The problem is that there is a bug in the logic behind TLMDDockPanel.UpdateTracking and how it executes from that point onwards.
Basically, it ends up calling TFloatingSite.PanelButtonClick, and that internally has what I think is the bug.
procedure TFloatingSite.PanelButtonClick(APanel: TLMDDockPanel; B: TLMDPanelBtnKind);
var
sgl: TLMDDockPanel;
begin
if (GetFormKind(sgl) <> ffPanel) or (APanel = sgl) then
begin
if not HeaderBtnClick(B) then
inherited;
end
else
inherited;
end;
The
if (GetFormKind(sgl) <> ffPanel) or (APanel = sgl) then
code is insufficient in my view, and that is causing the HeaderBtnClick(B)to run and that not only closes the whole floating form (FForm.close), but also returns false and doesn't let the ffPanel.Close execute inside the inherited part.
This code must correctly differentiate if it's being triggered by the close button of a Panel or of the FloatingForm, and it's failing that.
I fixed it by adding a parameter to PanelButtonClick to indicate that sender:
procedure TFloatingSite.PanelButtonClick(APanel: TLMDDockPanel; B: TLMDPanelBtnKind; pPanelBtn : Boolean);
and then internally the IF code becomes this:
procedure TFloatingSite.PanelButtonClick(APanel: TLMDDockPanel; B: TLMDPanelBtnKind; pPanelBtn : Boolean);
var
sgl: TLMDDockPanel;
begin
if (GetFormKind(sgl) <> ffPanel) or (APanel = sgl) then
begin
if (pPanelBtn) or (not HeaderBtnClick(B)) then
inherited;
end
else
inherited;
end;
Now the only thing missing is to call that method with the right parameter at the right time:
procedure TLMDDockPanel.UpdateTracking(AMouseDown, AMouseUp: Boolean);
begin
(...)
FSite.PanelButtonClick(Self, btn, True);
(...)
end;
And for the DockingSite one:
procedure TLMDDockSite.MouseUp(Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
(...)
PanelButtonClick(pnl, pbClose, False);
(...)
end;
And everything works as I think it should.
var
sgl: TLMDDockPanel;
begin
if (GetFormKind(sgl) = ffPanel) and (APanel = sgl) then
begin
if not HeaderBtnClick(B) then
inherited;
end
else
inherited;
end;