From 3d1ee5f1740d4d1ea0c6e0e107bc063c0a066179 Mon Sep 17 00:00:00 2001 From: juha Date: Sun, 6 Aug 2017 18:57:23 +0000 Subject: [PATCH] AnchorDocking: Implement AnchorDockPanel. Issue #24703, patch from Andrey Zubarev. git-svn-id: trunk@55638 - --- .gitattributes | 2 + components/anchordocking/anchordocking.lpk | 8 +- components/anchordocking/anchordocking.pas | 203 +++++++++++++----- components/anchordocking/anchordockpanel.pas | 71 ++++++ .../anchordocking/anchordockpanel_icon.lrs | 9 + components/anchordocking/anchordockpkg.pas | 10 +- .../anchordocking/anchordockstorage.pas | 9 +- 7 files changed, 259 insertions(+), 53 deletions(-) create mode 100644 components/anchordocking/anchordockpanel.pas create mode 100644 components/anchordocking/anchordockpanel_icon.lrs diff --git a/.gitattributes b/.gitattributes index b91b8fb835..ed55f45743 100644 --- a/.gitattributes +++ b/.gitattributes @@ -560,6 +560,8 @@ components/anchordocking/anchordocking.lpk svneol=native#text/plain components/anchordocking/anchordocking.pas svneol=native#text/plain components/anchordocking/anchordockoptionsdlg.lfm svneol=native#text/plain components/anchordocking/anchordockoptionsdlg.pas svneol=native#text/plain +components/anchordocking/anchordockpanel.pas svneol=native#text/pascal +components/anchordocking/anchordockpanel_icon.lrs svneol=native#text/pascal components/anchordocking/anchordockpkg.pas svneol=native#text/plain components/anchordocking/anchordockstorage.pas svneol=native#text/plain components/anchordocking/anchordockstr.pas svneol=native#text/plain diff --git a/components/anchordocking/anchordocking.lpk b/components/anchordocking/anchordocking.lpk index 9f2a17ee1a..d2c70ac8df 100644 --- a/components/anchordocking/anchordocking.lpk +++ b/components/anchordocking/anchordocking.lpk @@ -2,6 +2,7 @@ + @@ -18,7 +19,7 @@ - + @@ -48,6 +49,11 @@ + + + + + diff --git a/components/anchordocking/anchordocking.pas b/components/anchordocking/anchordocking.pas index 5cc6a000ec..1082b14195 100644 --- a/components/anchordocking/anchordocking.pas +++ b/components/anchordocking/anchordocking.pas @@ -102,7 +102,7 @@ uses LCLType, LCLIntf, LCLProc, Controls, Forms, ExtCtrls, ComCtrls, Graphics, Themes, Menus, Buttons, LazConfigStorage, Laz2_XMLCfg, LazFileCache, - AnchorDockStr, AnchorDockStorage; + AnchorDockStr, AnchorDockStorage, AnchorDockPanel; {$IFDEF DebugDisableAutoSizing} const ADAutoSizingReason = 'TAnchorDockMaster Delayed'; @@ -308,7 +308,7 @@ type OnlyCheckIfPossible: boolean): boolean; function EnlargeSideRotateSplitter(Side: TAnchorKind; OnlyCheckIfPossible: boolean): boolean; - procedure CreateBoundSplitter; + procedure CreateBoundSplitter(Disabled: boolean=false); procedure PositionBoundSplitter; public constructor CreateNew(AOwner: TComponent; Num: Integer = 0); override; @@ -592,6 +592,8 @@ type procedure MakeDockSite(AForm: TCustomForm; Sites: TAnchors; ResizePolicy: TADMResizePolicy; AllowInside: boolean = false); + procedure MakeDockPanel(APanel:TAnchorDockPanel; + ResizePolicy: TADMResizePolicy); procedure MakeVisible(AControl: TControl; SwitchPages: boolean); function ShowControl(ControlName: string; BringToFront: boolean = false): TControl; procedure CloseAll; @@ -1573,7 +1575,7 @@ procedure TAnchorDockMaster.MapTreeToControls(Tree: TAnchorDockLayoutTree); end; if (Node.NodeType=adltnCustomSite) then begin AControl:=FindControl(Node.Name); - if IsCustomSite(AControl) then + if IsCustomSite(AControl) or (AControl is TAnchorDockPanel) then fTreeNameToDocker[Node.Name]:=AControl; end; for i:=0 to Node.Count-1 do @@ -1649,6 +1651,11 @@ procedure TAnchorDockMaster.MapTreeToControls(Tree: TAnchorDockLayoutTree); if (fTreeNameToDocker[Node.Name]=nil) and (BestSite<>nil) then begin // search the parent site of a child site repeat + if BestSite is TAnchorDockPanel then begin + if fTreeNameToDocker.ControlToName(BestSite)='' then + fTreeNameToDocker[Node.Name]:=BestSite; + break; + end; BestSite:=BestSite.Parent; if BestSite is TAnchorDockHostSite then begin if fTreeNameToDocker.ControlToName(BestSite)='' then @@ -1760,15 +1767,19 @@ var aMonitor: TMonitor; aHostSite: TAnchorDockHostSite; begin - if Parent=nil then begin - if (Node.Monitor>=0) and (Node.Monitornil then raise Exception.Create('TAnchorDockMaster.MakeDockSite DockManager<>nil'); @@ -2735,6 +2757,34 @@ begin end; end; +procedure TAnchorDockMaster.MakeDockPanel(APanel:TAnchorDockPanel; + ResizePolicy: TADMResizePolicy); +var + AManager: TAnchorDockManager; +begin + if APanel.Name='' then + raise Exception.Create('TAnchorDockMaster.MakeDockPanel '+ + adrsMissingControlName); + if APanel.DockManager<>nil then + raise Exception.Create('TAnchorDockMaster.MakeDockPanel DockManager<>nil'); + APanel.DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.MakeDockPanel'){$ENDIF}; + try + if FControls.IndexOf(APanel)<0 then begin + FControls.Add(APanel); + APanel.FreeNotification(Self); + end; + AManager:=ManagerClass.Create(APanel); + AManager.DockableSites:=[]; + AManager.InsideDockingAllowed:=true; + AManager.ResizePolicy:=ResizePolicy; + APanel.DockManager:=AManager; + APanel.UseDockManager:=true; + APanel.DockSite:=true; + finally + APanel.EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TAnchorDockMaster.MakeDockPanel'){$ENDIF}; + end; +end; + procedure TAnchorDockMaster.MakeVisible(AControl: TControl; SwitchPages: boolean); begin while AControl<>nil do begin @@ -2808,6 +2858,22 @@ begin end; end; +function GetParentFormOrDockPanel(Control: TControl): TCustomForm; +begin + while (Control <> nil) and (Control.Parent <> nil) do + begin + if (Control is TAnchorDockPanel) then + Break; + Control := Control.Parent; + end; + if Control is TCustomForm then + Result := TCustomForm(Control) + else if Control is TAnchorDockPanel then + Result := TCustomForm(Control) + else + Result := nil; +end; + procedure TAnchorDockMaster.SaveMainLayoutToTree(LayoutTree: TAnchorDockLayoutTree); var i: Integer; @@ -2817,6 +2883,28 @@ var LayoutNode: TAnchorDockLayoutTreeNode; AForm: TCustomForm; VisibleControls: TStringList; + + procedure SaveForm(theForm: TCustomForm; SaveChildren: boolean); + begin + // custom dock site + LayoutNode:=LayoutTree.NewNode(LayoutTree.Root); + LayoutNode.NodeType:=adltnCustomSite; + LayoutNode.Assign(theForm,TObject(theForm) is TAnchorDockPanel); + // can have one normal dock site + if SaveChildren then + begin + Site:=TAnchorDockManager(theForm.DockManager).GetChildSite; + if Site<>nil then begin + LayoutNode:=LayoutTree.NewNode(LayoutNode); + Site.SaveLayout(LayoutTree,LayoutNode); + {if Site.BoundSplitter<>nil then begin + LayoutNode:=LayoutTree.NewNode(LayoutNode); + Site.BoundSplitter.SaveLayout(LayoutNode); + end;} + end; + end; + end; + begin SavedSites:=TFPList.Create; VisibleControls:=TStringList.Create; @@ -2825,31 +2913,22 @@ begin AControl:=Controls[i]; if not DockedControlIsVisible(AControl) then continue; VisibleControls.Add(AControl.Name); - AForm:=GetParentForm(AControl); + AForm:=GetParentFormOrDockPanel(AControl); if AForm=nil then continue; if SavedSites.IndexOf(AForm)>=0 then continue; SavedSites.Add(AForm); debugln(['TAnchorDockMaster.SaveMainLayoutToTree AForm=',DbgSName(AForm)]); DebugWriteChildAnchors(AForm,true,true); - if (AForm is TAnchorDockHostSite) then begin + if TObject(AForm) is TAnchorDockPanel then begin + SaveForm(GetParentFormOrDockPanel(AForm),{false}true); + //LayoutNode:=LayoutTree.NewNode(LayoutTree.Root); + //TAnchorDockPanel(AForm).SaveLayout(LayoutTree,LayoutNode); + end else if AForm is TAnchorDockHostSite then begin Site:=TAnchorDockHostSite(AForm); LayoutNode:=LayoutTree.NewNode(LayoutTree.Root); Site.SaveLayout(LayoutTree,LayoutNode); end else if IsCustomSite(AForm) then begin - // custom dock site - LayoutNode:=LayoutTree.NewNode(LayoutTree.Root); - LayoutNode.NodeType:=adltnCustomSite; - LayoutNode.Assign(AForm); - // can have one normal dock site - Site:=TAnchorDockManager(AForm.DockManager).GetChildSite; - if Site<>nil then begin - LayoutNode:=LayoutTree.NewNode(LayoutNode); - Site.SaveLayout(LayoutTree,LayoutNode); - {if Site.BoundSplitter<>nil then begin - LayoutNode:=LayoutTree.NewNode(LayoutNode); - Site.BoundSplitter.SaveLayout(LayoutNode); - end;} - end; + SaveForm(AForm,true); end else raise EAnchorDockLayoutError.Create('invalid root control for save: '+DbgSName(AControl)); end; @@ -2867,9 +2946,11 @@ var LayoutNode: TAnchorDockLayoutTreeNode; Site: TAnchorDockHostSite; begin - if (AForm is TAnchorDockHostSite) then begin + if AForm is TAnchorDockHostSite then begin Site:=TAnchorDockHostSite(AForm); Site.SaveLayout(LayoutTree,LayoutTree.Root); + end else if TObject(AForm) is TAnchorDockPanel then begin + (TObject(AForm) as TAnchorDockPanel).SaveLayout(LayoutTree,LayoutTree.Root); end else if IsCustomSite(AForm) then begin LayoutTree.Root.NodeType:=adltnCustomSite; LayoutTree.Root.Assign(AForm); @@ -2908,7 +2989,7 @@ var begin if not IsSite(AControl) then raise Exception.Create('TAnchorDockMaster.CreateRestoreLayout: not a site '+DbgSName(AControl)); - AForm:=GetParentForm(AControl); + AForm:=GetParentFormOrDockPanel(AControl); Result:=TAnchorDockRestoreLayout.Create(TAnchorDockLayoutTree.Create); if AForm=nil then exit; SaveSiteLayoutToTree(AForm,Result.Layout); @@ -4805,13 +4886,19 @@ begin end; end; -procedure TAnchorDockHostSite.CreateBoundSplitter; +procedure TAnchorDockHostSite.CreateBoundSplitter(Disabled: boolean); begin if BoundSplitter<>nil then exit; FBoundSplitter:=DockMaster.CreateSplitter; BoundSplitter.FreeNotification(Self); BoundSplitter.Align:=Align; BoundSplitter.Parent:=Parent; + if Disabled then + begin + BoundSplitter.Width:=0; + BoundSplitter.Height:=0; + BoundSplitter.Visible:=false; + end; end; procedure TAnchorDockHostSite.PositionBoundSplitter; @@ -5573,10 +5660,13 @@ begin ChildSite:=nil; if Child is TAnchorDockHostSite then begin ChildSite:=TAnchorDockHostSite(Child); - ChildSite.CreateBoundSplitter; + ChildSite.CreateBoundSplitter(Site is TAnchorDockPanel); SplitterWidth:=DockMaster.SplitterWidth; end; + if Site is TAnchorDockPanel then + ADockObject.DropAlign:=alClient; + // resize Site NewSiteBounds:=Site.BoundsRect; case ADockObject.DropAlign of @@ -5584,14 +5674,18 @@ begin alRight: dec(NewSiteBounds.Right,Child.ClientWidth+SplitterWidth); alTop: dec(NewSiteBounds.Top,Child.ClientHeight+SplitterWidth); alBottom: inc(NewSiteBounds.Bottom,Child.ClientHeight+SplitterWidth); + alClient: ; end; if not StoredConstraintsValid then StoreConstraints; if ADockObject.DropAlign in [alLeft,alRight] then Site.Constraints.MaxWidth:=0 - else + else if ADockObject.DropAlign in [alTop,alBottom] then Site.Constraints.MaxHeight:=0; Site.BoundsRect:=NewSiteBounds; + if ADockObject.DropAlign=alClient then + Child.Align:=alClient; + //debugln(['TAnchorDockManager.InsertControl Site.BoundsRect=',dbgs(Site.BoundsRect),' NewSiteBounds=',dbgs(NewSiteBounds),' Child.ClientRect=',dbgs(Child.ClientRect)]); FSiteClientRect:=Site.ClientRect; @@ -5604,8 +5698,11 @@ begin alLeft: NewChildBounds:=Bounds(0,0,Child.ClientWidth,Site.ClientHeight); alRight: NewChildBounds:=Bounds(Site.ClientWidth-Child.ClientWidth,0, Child.ClientWidth,Site.ClientHeight); + alClient: NewChildBounds:=Bounds(0,0, + Site.ClientWidth,Site.ClientHeight); end; Child.BoundsRect:=NewChildBounds; + NewChildBounds:=Child.BoundsRect; if ChildSite<>nil then ChildSite.PositionBoundSplitter; @@ -5669,6 +5766,12 @@ begin or (Site.Parent.Parent<>nil) then Inside:=true; end; + + if Site is TAnchorDockPanel then begin + DockRect:=Bounds(Site.ClientOrigin.x,Site.ClientOrigin.y,Site.ClientWidth,Site.ClientHeight); + exit; + end; + case DropAlign of alLeft: if Inside then @@ -5834,14 +5937,18 @@ var case ResizePolicy of admrpChild: begin - if Child.Align in [alLeft,alRight] then - Child.Width:=Max(1,Min(ChildMaxSize.X,Child.Width+WidthDiff)) + if Child.Parent is TAnchorDockPanel then + // else begin - i:=Max(1,Min(ChildMaxSize.Y,Child.Height+HeightDiff)); - {$IFDEF VerboseAnchorDockRestore} - debugln(['TAnchorDockManager.ResetBounds Child=',DbgSName(Child),' OldHeight=',Child.Height,' NewHeight=',i]); - {$ENDIF} - Child.Height:=i; + if Child.Align in [alLeft,alRight] then + Child.Width:=Max(1,Min(ChildMaxSize.X,Child.Width+WidthDiff)) + else begin + i:=Max(1,Min(ChildMaxSize.Y,Child.Height+HeightDiff)); + {$IFDEF VerboseAnchorDockRestore} + debugln(['TAnchorDockManager.ResetBounds Child=',DbgSName(Child),' OldHeight=',Child.Height,' NewHeight=',i]); + {$ENDIF} + Child.Height:=i; + end; end; end; end; diff --git a/components/anchordocking/anchordockpanel.pas b/components/anchordocking/anchordockpanel.pas new file mode 100644 index 0000000000..91e80a4cd8 --- /dev/null +++ b/components/anchordocking/anchordockpanel.pas @@ -0,0 +1,71 @@ +{ For license see anchordocking.pas +} +unit AnchorDockPanel; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, LResources, Forms, Controls, Graphics, Dialogs, ExtCtrls, + AnchorDockStorage; + +type + TAnchorDockPanel = class(TPanel) + protected + procedure DragOver({%H-}Source: TObject; {%H-}X, {%H-}Y: Integer; {%H-}State: TDragState; + var Accept: Boolean); override; + public + procedure SaveLayout(LayoutTree: TAnchorDockLayoutTree; + LayoutNode: TAnchorDockLayoutTreeNode); + function GetOneControl: TControl; + published + end; + +procedure Register; + +implementation + +uses AnchorDocking; + +procedure TAnchorDockPanel.DragOver(Source: TObject; X, Y: Integer; State: TDragState; + var Accept: Boolean); +begin + Accept:=true; +end; + +function TAnchorDockPanel.GetOneControl: TControl; +var + i: Integer; +begin + for i:=0 to ControlCount-1 do begin + Result:=Controls[i]; + if Result.Owner<>Self then exit; + end; + Result:=nil; +end; + +procedure TAnchorDockPanel.SaveLayout( + LayoutTree: TAnchorDockLayoutTree; LayoutNode: TAnchorDockLayoutTreeNode); +var + OneControl: TControl; +begin + OneControl:=GetOneControl; + if OneControl is TAnchorDockHostSite then + begin + + LayoutNode.NodeType:=adltnControl; + LayoutNode.Assign(Self); + LayoutNode.Name:={OneControl.}Name; + + TAnchorDockHostSite(OneControl).SaveLayout(LayoutTree,LayoutNode); + end; +end; + +procedure Register; +begin + {$I anchordockpanel_icon.lrs} + RegisterComponents('Additional',[TAnchorDockPanel]); +end; + +end. diff --git a/components/anchordocking/anchordockpanel_icon.lrs b/components/anchordocking/anchordockpanel_icon.lrs new file mode 100644 index 0000000000..60ab7c2501 --- /dev/null +++ b/components/anchordocking/anchordockpanel_icon.lrs @@ -0,0 +1,9 @@ +LazarusResources.Add('TAnchorDockPanel','PNG',[ + #137'PNG'#13#10#26#10#0#0#0#13'IHDR'#0#0#0#24#0#0#0#24#8#6#0#0#0#224'w='#248#0 + +#0#0'xIDATx^'#237#149'A'#10#132'0'#12'E'#127#134'^w'#214#230#8#174#167#23#26 + +#157'k'#204#162'"x'#140'~Q'#232#166#184#146'F'#4#243' '#144#213#127#132'@"9g' + +#136#8#172#16#146#176'$'#148'F563'#245#253'[P '#185'W'#215'}'#216#130'-g'#252 + +#14','#185#1#21')'#253'q'#150'yZP'#243#130#1#143#19#184#192#5'.pA88'#185'v'#2 + +#213'h3Ays'#191'a'#228#213'O'#255#254'K^'#1#166'zsS'#138#185#218'6'#0#0#0#0 + +'IEND'#174'B`'#130 +]); diff --git a/components/anchordocking/anchordockpkg.pas b/components/anchordocking/anchordockpkg.pas index 2254c03124..9249161f30 100644 --- a/components/anchordocking/anchordockpkg.pas +++ b/components/anchordocking/anchordockpkg.pas @@ -8,8 +8,16 @@ unit anchordockpkg; interface uses - AnchorDocking, AnchorDockStorage, AnchorDockStr, AnchorDockOptionsDlg; + AnchorDocking, AnchorDockStorage, AnchorDockStr, AnchorDockOptionsDlg, + AnchorDockPanel, LazarusPackageIntf; implementation +procedure Register; +begin + RegisterUnit('AnchorDockPanel', @AnchorDockPanel.Register); +end; + +initialization + RegisterPackage('AnchorDocking', @Register); end. diff --git a/components/anchordocking/anchordockstorage.pas b/components/anchordocking/anchordockstorage.pas index c83ba36944..f4f1c77185 100644 --- a/components/anchordocking/anchordockstorage.pas +++ b/components/anchordocking/anchordockstorage.pas @@ -113,7 +113,7 @@ type procedure Clear; function IsEqual(Node: TAnchorDockLayoutTreeNode): boolean; procedure Assign(Node: TAnchorDockLayoutTreeNode); overload; - procedure Assign(AControl: TControl); overload; + procedure Assign(AControl: TControl; OverrideBoundsRect: Boolean=false); overload; procedure LoadFromConfig(Config: TConfigStorage); overload; procedure LoadFromConfig(Path: string; Config: TRttiXMLConfig); overload; procedure SaveToConfig(Config: TConfigStorage); overload; @@ -1113,13 +1113,16 @@ begin end; end; -procedure TAnchorDockLayoutTreeNode.Assign(AControl: TControl); +procedure TAnchorDockLayoutTreeNode.Assign(AControl: TControl; OverrideBoundsRect: Boolean=false); var AnchorControl: TControl; a: TAnchorKind; begin Name:=AControl.Name; - BoundsRect:=AControl.BoundsRect; + if OverrideBoundsRect then + BoundsRect:=GetParentForm(AControl).BoundsRect + else + BoundsRect:=AControl.BoundsRect; Align:=AControl.Align; if (AControl.Parent=nil) and (AControl is TCustomForm) then begin WindowState:=TCustomForm(AControl).WindowState;