dockmanager example: improved streaming interface

git-svn-id: trunk@25039 -
This commit is contained in:
dodi 2010-04-28 17:34:19 +00:00
parent c4206d5f9a
commit eab9a88328

View File

@ -16,11 +16,9 @@ Possible extensions:
- separate docking management and dock site layout - separate docking management and dock site layout
+ various dock headers + various dock headers
- multiple splitters (on zones without controls) - multiple splitters (on zones without controls)
+ persistence (requires application wide management of dock sources!) + persistence (requires application-wide management of dock sources!)
- purpose of Restore button? - purpose of Restore button?
- purpose of Close button? (currently: undock)
Known bugs:
+ Problem with dragging header, seems to interfere with dragmanager (capture)?
More issues, concerning the rest of the LCL (mainly unit Controls): More issues, concerning the rest of the LCL (mainly unit Controls):
@ -53,7 +51,7 @@ LCL TODO:
{$mode objfpc}{$H+} {$mode objfpc}{$H+}
{$DEFINE ctlType} //save <name>:<classname> {$DEFINE ctlType} //save <name>:<classname>=<caption>
{$DEFINE RootDock} //allow docking into the root zone? {$DEFINE RootDock} //allow docking into the root zone?
//{$DEFINE newSplitter} //exclude splitter from remaining zone //{$DEFINE newSplitter} //exclude splitter from remaining zone
{.$DEFINE handle_existing} //dock controls existing in the dock site? {.$DEFINE handle_existing} //dock controls existing in the dock site?
@ -276,22 +274,26 @@ type
1) TCustomDockSite (notebooks...) 1) TCustomDockSite (notebooks...)
2) AppLoadStore 2) AppLoadStore
3) OnSave/Reload 3) OnSave/Reload
4) default (Delphi) compatible handling. 4) Owner of DockMaster (Application)
5) default (Delphi) compatible handling.
Customization is provided by OnSave/Restore handlers. Customization is provided by OnSave/Restore handlers.
Further customization is feasable using the AppLoadStore variable (below). Further customization is feasable using the AppLoadStore variable (below).
At the time of an call, DockLoader contains the full control description
(ControlDescriptor)
The DockLoader variable is initialized to a default TCustomDockMaster instance, The DockLoader variable is initialized to a default TCustomDockMaster instance,
but can be overridden by the application. This variable is not free'd. owned by Application. It can be overridden by the application.
uMakeSite.DockMaster represents the full interface. uMakeSite.DockMaster represents the full interface.
*) *)
RControl = record RControlDescriptor = record
Ctrl: TControl;
Name, ClassName, Caption: string; Name, ClassName, Caption: string;
end; end;
TCustomDockMaster = class(TComponent) TCustomDockMaster = class(TComponent)
protected protected
rc: RControl; rc: RControlDescriptor;
FOnSave: TOnSaveControl; FOnSave: TOnSaveControl;
FOnRestore: TOnReloadControl; FOnRestore: TOnReloadControl;
procedure CtlToRec(ctl: TControl); procedure CtlToRec(ctl: TControl);
@ -303,6 +305,7 @@ type
function ReloadControl(const AName: string; Site: TWinControl): TControl; virtual; function ReloadControl(const AName: string; Site: TWinControl): TControl; virtual;
property OnSave: TOnSaveControl read FOnSave write FOnSave; property OnSave: TOnSaveControl read FOnSave write FOnSave;
property OnRestore: TOnReloadControl read FOnRestore write FOnRestore; property OnRestore: TOnReloadControl read FOnRestore write FOnRestore;
property ControlDescriptor: RControlDescriptor read rc;
end; end;
var var
DockLoader: TCustomDockMaster; DockLoader: TCustomDockMaster;
@ -341,6 +344,7 @@ function NoteBookCreate: TCustomDockSite;
procedure NoteBookAdd(ABook: TCustomDockSite; AItem: TControl); //to be removed procedure NoteBookAdd(ABook: TCustomDockSite; AItem: TControl); //to be removed
function TryRename(AComp: TComponent; const NewName: string): boolean; function TryRename(AComp: TComponent; const NewName: string): boolean;
function CreateUniqueComponentName(const AClassName: string; OwnerComponent: TComponent): string; function CreateUniqueComponentName(const AClassName: string; OwnerComponent: TComponent): string;
function FindOwnedComponent(const AName: string; AOwner: TComponent): TComponent;
const const
CustomDockSiteID = 1; CustomDockSiteID = 1;
@ -430,11 +434,27 @@ begin
Result.Bottom := Result.Top + ACtrl.Height; Result.Bottom := Result.Top + ACtrl.Height;
end; end;
function FindOwnedComponent(const AName: string; AOwner: TComponent): TComponent;
var
i: integer;
begin
if assigned(AOwner) then begin
for i := 0 to AOwner.ComponentCount - 1 do begin
Result := AOwner.Components[i];
if CompareText(Result.Name, AName) = 0 then
exit; //found
end;
end;
//not found
Result := nil;
end;
//from CustomFormEditor.pp //from CustomFormEditor.pp
function {TCustomFormEditor.}CreateUniqueComponentName(const AClassName: string; function {TCustomFormEditor.}CreateUniqueComponentName(const AClassName: string;
OwnerComponent: TComponent): string; OwnerComponent: TComponent): string;
var var
i, j: integer; inst: integer;
basename: string;
begin begin
(* Add instance number until unique. (* Add instance number until unique.
<T><basename> ==> <basename><_><number> <T><basename> ==> <basename><_><number>
@ -442,24 +462,22 @@ begin
and stripping trailing digits and (optionally) '_'. and stripping trailing digits and (optionally) '_'.
*) *)
Result:=AClassName; Result:=AClassName;
if (OwnerComponent=nil) or (Result='') then exit; //strip leading 'T'
i:=1; if (length(Result)>1) and (Result[1]='T') then
Result:=RightStr(Result,length(Result)-1);
if (OwnerComponent=nil) or (Result='') then
exit;
//if trailing digit, append '_'
if Result[length(Result)] in ['0'..'9'] then
Result:=Result+'_';
basename := Result;
inst := 1;
while true do begin while true do begin
Result:=AClassName;
//strip leading 'T'
if (length(Result)>1) and (Result[1]='T') then
Result:=RightStr(Result,length(Result)-1);
//if trailing digit, append '_'
if Result[length(Result)] in ['0'..'9'] then
Result:=Result+'_';
Result:=Result+IntToStr(i);
//append instance number //append instance number
j:=OwnerComponent.ComponentCount-1; Result := basename + IntToStr(inst);
while (j>=0) if FindOwnedComponent(Result, OwnerComponent) = nil then
and (CompareText(Result,OwnerComponent.Components[j].Name)<>0) do break;
dec(j); inc(inst);
if j<0 then exit;
inc(i);
end; end;
end; end;
@ -1164,7 +1182,7 @@ procedure TEasyTree.LoadFromStream(Stream: TStream);
*) *)
for i := FDockSite.DockClientCount - 1 downto 0 do begin for i := FDockSite.DockClientCount - 1 downto 0 do begin
ctl := FDockSite.DockClients[i]; ctl := FDockSite.DockClients[i];
ctl.Visible := True; ctl.Visible := True; //False?
ctl.ManualDock(nil); //restore undocked position and extent ctl.ManualDock(nil); //restore undocked position and extent
end; end;
end; end;
@ -1195,7 +1213,7 @@ procedure TEasyTree.LoadFromStream(Stream: TStream);
if NewCtl <> nil then begin if NewCtl <> nil then begin
NewCtl.Visible := True; NewCtl.Visible := True;
NewZone.ChildControl := NewCtl; NewZone.ChildControl := NewCtl;
SetReplacingControl(NewCtl); SetReplacingControl(NewCtl); //prevent DockManager actions
NewCtl.ManualDock(DockSite); NewCtl.ManualDock(DockSite);
NewCtl.Width := ZoneRec.BottomRight.x; NewCtl.Width := ZoneRec.BottomRight.x;
NewCtl.Height := ZoneRec.BottomRight.y; NewCtl.Height := ZoneRec.BottomRight.y;
@ -1263,7 +1281,7 @@ procedure TEasyTree.SaveToStream(Stream: TStream);
if child = nil then if child = nil then
ZoneName := '' ZoneName := ''
else else
ZoneName := SaveDockedControl(child); ZoneName := SaveDockedControl(child); //ctrl or entire site
//write descriptor //write descriptor
Stream.Write(ZoneRec, sizeof(ZoneRec)); Stream.Write(ZoneRec, sizeof(ZoneRec));
Stream.WriteAnsiString(ZoneName); Stream.WriteAnsiString(ZoneName);
@ -1272,7 +1290,6 @@ procedure TEasyTree.SaveToStream(Stream: TStream);
DoSaveZone(Zone.FirstChild, Level + 1); //all children of Level DoSaveZone(Zone.FirstChild, Level + 1); //all children of Level
// recurse into next sibling // recurse into next sibling
Zone := Zone.NextSibling; Zone := Zone.NextSibling;
//if Zone <> nil then DoSaveZone(Zone, Level); //all siblings of Level
end; end;
end; end;
@ -1281,7 +1298,6 @@ begin
DoSaveZone(FTopZone, 1); DoSaveZone(FTopZone, 1);
//write end marker (dummy record of level 0) //write end marker (dummy record of level 0)
ZoneRec.Level := 0; ZoneRec.Level := 0;
//ZoneRec.NameLen := 0;
Stream.Write(ZoneRec, sizeof(ZoneRec)); Stream.Write(ZoneRec, sizeof(ZoneRec));
end; end;
@ -2168,15 +2184,17 @@ end;
procedure TCustomDockMaster.CtlToRec(ctl: TControl); procedure TCustomDockMaster.CtlToRec(ctl: TControl);
begin begin
//fill record with all names //fill record with all names
rc.Ctrl := ctl;
rc.ClassName := ctl.ClassName; rc.ClassName := ctl.ClassName;
rc.Name := ctl.Name; rc.Name := ctl.Name;
rc.Caption := ctl.Caption; //obsolete rc.Caption := ctl.Caption; //general purpose
end; end;
procedure TCustomDockMaster.IdToRec(const ID: string); procedure TCustomDockMaster.IdToRec(const ID: string);
var var
i: integer; i: integer;
begin begin
rc.Ctrl := nil; //flag: not yet found/created
//split <Name>':'<ClassName>'='<Caption> //split <Name>':'<ClassName>'='<Caption>
i := Pos(':', ID); i := Pos(':', ID);
if i > 0 then begin if i > 0 then begin
@ -2231,11 +2249,12 @@ begin
- saved site info (if CustomDockSite) - saved site info (if CustomDockSite)
- AppLoadStore - AppLoadStore
- OnRestore - OnRestore
- Owner
- create from descriptor - create from descriptor
- DockSite - DockSite
*) *)
Result := nil; Result := nil;
IdToRec(AName); IdToRec(AName); //make all information accessible to external subroutines
//first check for special CustomDockSite format //first check for special CustomDockSite format
if ord(AName[1]) = CustomDockSiteID then if ord(AName[1]) = CustomDockSiteID then
Result := TCustomDockSite.ReloadSite(AName, Site) Result := TCustomDockSite.ReloadSite(AName, Site)
@ -2246,18 +2265,25 @@ begin
Result := FOnRestore(rc.Name, Site); Result := FOnRestore(rc.Name, Site);
if Result <> nil then if Result <> nil then
exit; exit;
//check existing control
fo := Owner; //assume all freely dockable controls are owned by our owner (Application)
cmp := FindOwnedComponent(rc.Name, fo);
//if Result <> nil then
if Result is TControl then
exit;
Result := nil; //exclude non-controls
//load from descriptor //load from descriptor
fc := TWinControlClass(GetClass(rc.ClassName)); fc := TWinControlClass(GetClass(rc.ClassName));
if not assigned(fc) then begin if not assigned(fc) then begin
DebugLn(rc.ClassName, ' is not a registered class'); DebugLn(rc.ClassName, ' is not a registered class');
//exit(nil); //bad form name //exit(nil); //bad form name
end else begin end else begin
fo := Owner; //init from descriptor
Result := fc.Create(fo); Result := fc.Create(fo);
if Result.Name <> rc.Name then if Result.Name <> rc.Name then
TryRename(Result, rc.Name); TryRename(Result, rc.Name);
if Result.Caption <> rc.Caption then if Result.Caption <> rc.Caption then
Result.Caption := rc.Caption; Result.Caption := rc.Caption; //really?
end; end;
//last resort //last resort
if Result = nil then if Result = nil then
@ -2272,7 +2298,7 @@ initialization
DockLoader := TCustomDockMaster.Create(Application); DockLoader := TCustomDockMaster.Create(Application);
finalization finalization
DestroyDockHeaderImages; DestroyDockHeaderImages;
//FreeAndNil(LoadStore); //FreeAndNil(LoadStore); - by owner!
end. end.