Difference between revisions of "EasyDockingManager"

From Free Pascal wiki
Jump to navigationJump to search
Line 24: Line 24:
 
Use DockMaster.MakeDockable() to make any of your forms dockable. This includes setting the related properties (DragKind...), and a pin-shaped dock grip is added to the form. This grip is needed on platforms that do not (yet) support for dragging entire forms, its exact appearance and placement may be changed.
 
Use DockMaster.MakeDockable() to make any of your forms dockable. This includes setting the related properties (DragKind...), and a pin-shaped dock grip is added to the form. This grip is needed on platforms that do not (yet) support for dragging entire forms, its exact appearance and placement may be changed.
  
When a form doesn't deserve special initialization, DockMaster.CreateDockable() can be used to also create the dockable form, given a form name. Here the form name must correspond to a registered form class name, and it can include an instance number. E.g. "Form3" will create a form of class "TForm3" when fMultiInst is False. When fMultiInst is True, e.g. "DockForm2" will create a form of class "TDockForm", named "DockForm2".
+
When a form doesn't deserve special initialization, DockMaster.CreateDockable() can be used to also create the dockable form, given a form name. Here the form name must correspond to a registered form class name, and it can include an instance number. E.g. "Form3" will create a form of class "TForm3", named "TForm3_1" for the first instance.
  
 
==== Saving and Restoring a Layout ====
 
==== Saving and Restoring a Layout ====

Revision as of 09:14, 13 December 2009

Easy Docking Manager Package

The examples/EasyDockMgr package offers several helpers for docking support. This entry is organized in multiple levels, which cover different views on the docking topics. For docking basics see LCL_Drag_Drop#Common_Principles.

Please note: All mentioned names may change in a final release!

Also not all options are implemented now, see the #Further Options topic for issues deserving some considerations before.

Adding Docking Support to an entire Application

In the most simple case you make one or more forms of your application DockSites, to which other forms can be docked. Then make selected or all other forms dockable, and everything else will be done for you :-)

Prerequisites

The uMakeSite unit defines a DockMaster class and variable (singleton), that manages all docking issues. An application must create an instance of TDockMaster, before it can use its methods.

It should be possible to make some of the methods class methods or simple (non-object) functions, depending on the final handling of the dock sites which currently are owned by the DockMaster component. The DockMaster also could be created automatically, in the initialization section of the unit. Any opinions?

Making a Form a DockSite

Use DockMaster.AddElasticSites() to make a form a DockSite. Currently docking regions (panels) can be added to the left, right and bottom of a form. "Elastic" here means that the docking regions are almost invisible, as long as nothing is docked into them. When a first component is docked into such a region, the panel extends either within the form, reducing the form's client area, or it expands the form, depending on where exactly the component is dropped. The DockRect frame indicates which kind of expansion will occur on a drop. The size of every docking region can be adjusted later by moving its associated splitter.

Making a Form dockable

Use DockMaster.MakeDockable() to make any of your forms dockable. This includes setting the related properties (DragKind...), and a pin-shaped dock grip is added to the form. This grip is needed on platforms that do not (yet) support for dragging entire forms, its exact appearance and placement may be changed.

When a form doesn't deserve special initialization, DockMaster.CreateDockable() can be used to also create the dockable form, given a form name. Here the form name must correspond to a registered form class name, and it can include an instance number. E.g. "Form3" will create a form of class "TForm3", named "TForm3_1" for the first instance.

Saving and Restoring a Layout

If you want your application to restore the previous docked layout on the next start, DockMaster.SaveToStream() stores all required layout information in the given stream. It's your responsibility to provide an according TFileStream. You can store different layouts in different files, if you like.

DockMaster.LoadFromStream() restores the previously stored layout. This means that the elastic and floating dock sites are created, and the previously docked forms are searched or created, and then are docked into their previous places.

But how does the DockMaster know whether a docked form already exists, or whether a new instance has to be created? This requires that all dockable forms have the same Owner (TComponent), which includes a list of all already created dockable form or component instances. In default GUI applications the Application object is the Owner of all Forms, the Lazarus IDE instead uses a dedicated OwningComponent for this purpose.

Further Options

Above issues currently are reflected in $DEFINEs in uMakeSite, and in a DockMaster.Factory field. These and some more issues deserve further consideration (discussion). I'd like to remove the Defines and to replace them by meaningful conventions, suitable for all application cases.

When restored forms deserve further parameters, like editor components should reload the previously edited file, then the Delphi-inherited TWinControl.ReloadDockedControl(CtrlName) is insufficient. We could add an inverse method SaveDockedControl, that returns an string containing all information about a docked control, not only its name. This method can be added to TWinControl, or to a new TDockingFactory class used for DockMaster.Factory. When SaveDockedControl is implemented to return an string with more than only the control name, then of course that string must be handled in ReloadDockedControl, because the DockMaster has no idea how to extract the control name and class from such an string.

Another issue is notebook docking. The Delphi docking model offers no special means to restore DockBooks, neither by nesting DockSites nor by integration into an extended DockManager. The EasyDockTree manager already abuses alCustom for notebook docking, and could be extended to handle notebook contents (tabs), and eventually recursively the layout of notebook pages as further DockSites. Any opinions?

My docking notebooks differ from the IDE SourceEditor notebooks, at least in the handling of the docked pages. Since it depends on the platforms, which components can be made dockable how, I use the notebook tabs as docking grips. The pages of a DockBook are not related to each other, every dockable control can be docked as a notebook page, and can be undocked independently as well. Thus notebooks also can be docked into notebooks, what would allow e.g. to hold the included files of a source unit in a dedicated (nested) notebook, together with the unit source itself. OTOH the IDE SourceNotebook seems to be a specialized form class, handling only docked SourceEditor pages, what does not integrate well into the general layout Save/Load procedures. As long as the SourceNotebook form has no other components apart from the notebook itself, I'd suggest to refactor the SourceEditors into independent components (forms, frames...), that can be docked in whatever way the user likes.

IMO only one DefaultDockManager class should exist in an application. There exist conflicts between the current IDE implementation, using anchor docking as default, and the EasyDockMgr approach. Also only one default FloatingDockHost class should exist, what IMO suggests a strict separation between different docking models and managers. We could agree about an (abstract) application layout manager class, that would allow to use either anchor-docking or easy-docking in the IDE, by only exchanging the creation of the respective manager instance. Such an exchange will affect also the stored layouts, which are somewhat incompatible between different managers. Using XML configuration files would allow to use the same configuration file(s) for all managers, but the different information types IMO would make it still impossible to save a layout using one manager, and to restore it using an different manager.

Since anchor docking is merely a layout manager, not related to drag-dock, it should not touch any docking related defaults and procedures. This includes the removal of all special anchor-docking related code in TControl and TWinControl, that only can result in problems with other docking managers.

IDE Sample

This is an idea about making the IDE dockable. I couldn't make it work just now, but before it's forgotten. Let's assume that EnableIDEdocking defined means anchor docking, else new docking/layout.

In main.TMainIIDE the following case could be inserted:

TMainIIDE.Create - create DockMaster ...

 {$IFDEF EnableIDEdocking}
 FDockingManager:=TLazDockingManager.Create(Self); //anchor docking
 {$ELSE}
 uMakeSite.TDockMaster.Create(self); //easy docking: create DockMaster
 {$ENDIF}

TMainIIDE.OnlApplyWindowLayout - make forms dockable ...

 l:=NonModalIDEFormIDToEnum(ALayout.FormID);
 if DockingAllowed then begin
   if l in [nmiwSourceNoteBookName] then
     ALayout.WindowPlacement:=iwpDocked;
 end else if assigned(DockMaster) then begin
 //easy docking
   case l of
   nmiwNone: ;
   nmiwMainIDEName: ; //optional: DockMaster.AddElasticSites(ALayout.Form, [alBottom]);
   nmiwSourceNoteBookName: //make this one an elastic site
     DockMaster.AddElasticSites(ALayout.Form, [alLeft, alBottom, alRight]);
   else //make all other non-modal windows dockable
     DockMaster.MakeDockable(ALayout.Form, True);
   end;
 end;

The EasyDockTree Manager

Almost every DockSite needs an DockManager. The EasyDockTree manager allows to build an dock tree, that grows by docking other components to any side (top/bottom, left/right) of an already docked component. Dropping a component into the middle of another component creates a tabbed notebook, to which more components can be docked as notebook pages.

Docking Helper Classes

A FloatingSite is a container form for other components, that possibly cannot exist without a parent form (TControl). It also acts as an entire docking site (form), to which other components can be docked.

A DockBook also is a container form for other components, that keeps docked components in distinct notebook pages, while a FloatingSite shows the docked components beneath each other.