dockmanager example: Fixed most known bugs and flaws.

Allowed DockHeader to disappear for single client. (todo: also for notebook)
Disallowed undocking of a NOT docked notebook. (hack, the notebook should become a frame)

git-svn-id: trunk@22495 -
This commit is contained in:
dodi 2009-11-09 08:27:27 +00:00
parent 4d743bce3e
commit b76ca7675c
7 changed files with 136 additions and 35 deletions

View File

@ -3,11 +3,16 @@ unit fEditBook;
Move form to front whenever activated.
Dequeue form when destroyed.
The queue head is stored in the global variable MRUEdit;
The queue head is stored in the global variable MRUEdit.
The EditBook should become a frame, embeddable without docking.
*)
{$mode objfpc}{$H+}
{ TODO : figure out what's wrong with the mru list - with multiple windows }
{.$DEFINE mru} //problems with MRU list???
interface
uses
@ -27,7 +32,7 @@ type
end;
var
MRUEdit: TEditBook; //Most Rectently Used EditBook
MRUEdit: TEditBook; //Most Recently Used EditBook
implementation
@ -41,12 +46,15 @@ begin
if MRUEdit = Self then
exit; //is alread head
prev := MRUEdit;
{$IFDEF mru}
while (prev <> nil) and (prev.NRUEdit <> self) do
prev := prev.NRUEdit;
if prev <> nil then
prev.NRUEdit := self.NRUEdit; //was already in Q
NRUEdit := MRUEdit; //old head
MRUEdit := self; //become head
{$ELSE}
{$ENDIF}
end;
procedure TEditBook.FormClose(Sender: TObject; var CloseAction: TCloseAction);
@ -55,6 +63,7 @@ var
begin
//deQ self
prev := MRUEdit;
{$IFDEF mru}
if prev = self then
MRUEdit := NRUEdit
else begin
@ -64,6 +73,9 @@ begin
prev.NRUEdit := NRUEdit;
//else not in chain?
end;
NRUEdit := nil;
{$ELSE}
{$ENDIF}
end;
initialization

View File

@ -1,5 +1,3 @@
{ This is an automatically generated lazarus resource file }
LazarusResources.Add('TEditorSite','FORMDATA',[
'TPF0'#241#11'TEditorSite'#10'EditorSite'#4'Left'#3'b'#1#6'Height'#3','#1#5'W'
+'idth'#3#144#1#7'Caption'#6#10'EditorSite'#12'ClientHeight'#3#25#1#11'Client'

View File

@ -18,25 +18,16 @@ you can have multiple edit views within the editor window.
Secondary editor windows should have the same docking capabilities.
The View menu windows should be dockable to each other.
(Done, but the first dock clobbers the dock site - please redock)
Known bugs:
- The IDE suspects dangling references - KEEP these references!
Please report if you know how to fix this issue.
*)
(* Elastic mode
This mode currently works with a common flag in the form.
A more intuitive GUI would allow the user to determine the docking mode,
on the first drop into an elastic panel. Then the panel (or dockmanager) must
remember the mode, for undocking.
*)
{$mode objfpc}{$H+}
{.$DEFINE stdfloat} //using standard floating host?
interface
uses
@ -96,6 +87,8 @@ begin
FEdit.BorderStyle := bsNone;
FEdit.Parent := self;
FEdit.Visible := True;
FEdit.DragMode := dmManual; //disallow undocking
//FEdit.pnlDock.DragMode := dmManual;
FAutoExpand := True;
end;
@ -108,7 +101,6 @@ begin
Client.DragMode := dmAutomatic;
Client.DragKind := dkDock;
Client.Visible := True;
Client.FloatingDockSiteClass := TFloatingSite;
//name it
Client.Caption := cap;
try
@ -116,14 +108,18 @@ begin
except
//here: simply ignore duplicate name
end;
{$IFDEF old}
Client.Align := alClient; //required for proper docking
Client.ManualFloat(Rect(200,200, 400,400));
{$IFDEF stdfloat}
Client.ManualDock(nil);
{$ELSE}
Site := TFloatingSite.Create(Application);
//Site.Visible := True;
Client.FloatingDockSiteClass := TFloatingSite;
Client.ManualDock(Site, nil, alClient);
{$IFDEF old}
//ManualFloat doesn't work as expected :-(
//Client.Align := alClient; //required for proper docking
Client.ManualFloat(Rect(200,200, 400,400));
{$ELSE}
Site := TFloatingSite.Create(Application);
Client.ManualDock(Site, nil, alClient);
{$ENDIF}
{$ENDIF}
Result := Client;
end;
@ -144,7 +140,6 @@ end;
procedure TEditorSite.mnOpenClick(Sender: TObject);
begin
//OpenFile('fMain.pas');
if OpenDialog1.Execute then begin
OpenFile(OpenDialog1.FileName);
end;

View File

@ -8,7 +8,7 @@ To be added or ported:
Possible extensions:
- separate docking management and dock site layout
- various dock headers
+ various dock headers
- multiple splitters (on zones without controls)
- persistence (requires application wide management of dock sources!)
- purpose of Restore button?
@ -39,6 +39,10 @@ LCL TODO:
occur (perform LB_DOWN and LB_UP).
Otherwise dragging starts, and the control has to be reset into "no button down"
state.
The default floating site doesn't work properly.
When multiple clients are docked, and one of them should become floating,
the client is undocked BUT stays in the site.
*)
{$H+}
@ -88,7 +92,8 @@ type
TEasyHeaderStyle = (
hsMinimal, //Delphi style
hsForm //form style
hsForm, //form style
hsNone //no header (special notebook etc. style)
);
TEasyDockHeader = class
@ -196,10 +201,12 @@ type
//Lazarus extension
private
FHeader: TEasyDockHeader;
FHideSingleCaption: boolean;
FStyle: TEasyHeaderStyle;
FSplitter: TEasySplitter;
FSizeZone: TEasyZone; //zone to be resized, also PrevSibling
procedure SplitterMoved(Sender: TObject); //hide and reposition zone
procedure SetSingleCaption(Value: boolean);
public
procedure MessageHandler(Sender: TControl; var Message: TLMessage); override;
public
@ -208,9 +215,11 @@ type
{$ENDIF}
constructor Create(ADockSite: TWinControl); override;
destructor Destroy; override;
function DetectAlign(ZoneRect: TRect; MousePos: TPoint): TAlign;
function DetectAlign(ZoneRect: TRect; MousePos: TPoint): TAlign;
procedure PaintSite(DC: HDC); override;
procedure SetStyle(NewStyle: TEasyHeaderStyle);
function GetEffectiveStyle: TEasyHeaderStyle;
property HideSingleCaption: boolean read FHideSingleCaption write SetSingleCaption;
end;
const
@ -415,6 +424,26 @@ begin
end;
end;
function TEasyTree.GetEffectiveStyle: TEasyHeaderStyle;
begin
(* Handle suppression of single-client header.
DockSite.DockClientCount is not reliable at the time a control is being un/docked.
We could count our client controls, or take some more direct approach.
*)
//if FHideSingleCaption and (DockSite.DockClientCount <= 1) then begin
if FHideSingleCaption //and (ChildControlCount <= 1)
and ((FTopZone.FFirstChild = nil)
or ((FTopZone.FFirstChild.ChildControl <> nil)
and (FTopZone.FFirstChild.FNextSibling = nil)))
then begin
Result := hsNone; //single client should have no header
//DebugLn('client style: hsNone');
end else begin
Result := FStyle;
//DebugLn('zones style: %d', [FStyle]);
end;
end;
procedure TEasyTree.InsertControl(Control: TControl; InsertAt: TAlign;
DropCtl: TControl);
var
@ -984,6 +1013,16 @@ begin
//FReplacingControl := Control;
end;
procedure TEasyTree.SetSingleCaption(Value: boolean);
begin
(* Hide header if no more than one client is docked.
*)
if FHideSingleCaption = Value then
exit;
FHideSingleCaption := Value;
ResetBounds(True);
end;
procedure TEasyTree.SetStyle(NewStyle: TEasyHeaderStyle);
begin
if NewStyle = FStyle then
@ -1159,10 +1198,18 @@ end;
function TEasyZone.GetStyle: TEasyHeaderStyle;
begin
(* Get the effective header style.
A single notebook client deserves no header at all.
Other single clients can have no header (optional)
*)
{$IFDEF old}
if ChildControl is TEasyBook then
Result := hsMinimal //or none at all?
else
Result := FTree.FStyle;
{$ELSE}
Result := FTree.GetEffectiveStyle;
{$ENDIF}
end;
function TEasyZone.HasSizer: boolean;

View File

@ -26,6 +26,10 @@ into a form, before it can be dragged and docked by dragging the form.
Apply ToolButtonAutoSizeAlign.patch to improve the appearance and behaviour
of the toolbar buttons.
Problem:
Disallow undocking/floating of a NOT docked dockbook.
*)
{$mode objfpc}{$H+}
@ -169,10 +173,13 @@ begin
CurTab.Click;
end;
end else begin
//last tab removed
if HostDockSite <> nil then
ManualDock(nil); //undock before closing
Close;
//last tab removed - close ONLY if we are docked
//if (HostDockSite <> nil) or Floating then begin - Floating doesn't work
if Parent = nil then begin //seems to be a good indicator for floating state
if (HostDockSite <> nil) then //may be cleared already???
ManualDock(nil); //undock before closing
Release; //Close;
end;
end;
end;
@ -279,9 +286,11 @@ procedure TTabs.MouseMove(Shift: TShiftState; X, Y: Integer);
begin
(* Implement dragging of the entire notebook.
Parent is assumed to be the notebook form.
Try prevent undocking of NOT docked form.
*)
inherited MouseMove(Shift, X, Y);
if ssLeft in Shift then
//if ssLeft in Shift then
if (ssLeft in Shift) and (Parent.HostDockSite <> nil) then
Parent.BeginDrag(False); //delayed docking of the container form
end;

View File

@ -25,6 +25,7 @@ type
protected
function DoUnDock(NewTarget: TWinControl; Client: TControl;
KeepDockSiteSize: Boolean = true): Boolean; override;
procedure Loaded; override;
public
{ public declarations }
end;
@ -35,6 +36,7 @@ var
implementation
uses
EasyDockSite,
LCLproc;
{ TFloatingSite }
@ -58,6 +60,8 @@ end;
function TFloatingSite.DoUnDock(NewTarget: TWinControl; Client: TControl;
KeepDockSiteSize: Boolean): Boolean;
begin
(* Copied from TWinControl.DoUnDock - try fix flaws.
*)
//Result:=inherited DoUnDock(NewTarget, Client, KeepDockSiteSize);
Result := True;
if Assigned(OnUnDock) then begin
@ -98,6 +102,7 @@ existing) target.
Result := Result and DoUndockClientMsg(NewTarget, Client);
if Result and (NewTarget = nil) then begin
//ManualFloat(???)
{ TODO : Create floating dock site - but not here, the DockObject must be updated accordingly! }
end;
end;
@ -126,16 +131,28 @@ begin
Allow := False;
//move form?
end else
Application.ReleaseComponent(Self); //Close;
Release;
end else begin
//allow float - action required?
(* strange behaviour: client is undocked, but stays in the site.
The site is moved to the drop location.
*)
Allow := True;
Allow := NewTarget <> nil; //simply disallow undock to floating state (for now)
end;
if Allow then
if Allow then begin
AdjustCaption(Client);
end;
end;
procedure TFloatingSite.Loaded;
begin
inherited Loaded;
if DockManager = nil then
DockManager := TEasyTree.Create(self);
if DockManager is TEasyTree then begin
TEasyTree(DockManager).HideSingleCaption := True;
TEasyTree(DockManager).SetStyle(hsForm);
end;
end;
initialization

View File

@ -97,6 +97,18 @@ HeaderPartMap: array[TEasyHeaderStyle, TEasyZonePart] of TZonePartMap = (
(dTop:dBorder; dBottom:-dButton; dLeft:-dButton; dRight:dButton+2*dBorder) //zpRestoreButton, // header restore button
{$ENDIF}
(dTop:dBorder; dBottom:-dButton; dLeft:-dButton; dRight:dBorder) //zpCloseButton // header close button
),
//hsNone deserves special handling, the map is ignored
(
{zpNowhere} (),
{zpClient} (dTop:0; dBottom:0),
{zpAll} (dTop:0; dBottom:-dDHeader),
{zpCaption} (dTop:dDBorder; dBottom:-dDButton; dLeft:dDBorder; dRight:2*dDBorder+dDButton),
{zpSizer} (dTop:0; dBottom:-dSizer),
{$IFDEF restore}
{zpRestoreButton} (),
{$ENDIF}
{zpCloseButton} (dTop:dDBorder; dBottom:-dDButton; dLeft:-dDButton; dRight:dDBorder)
)
);
@ -134,13 +146,21 @@ begin
(* ARect is (must be) TLBR zone rectangle, on input.
HasSplitter determines whether to exclude the splitter from ARect,
and also the splitter area itself.
hsNone (no header) must be handled separately.
*)
if (APart = zpNowhere) or ((APart = zpSizer) and not HasSplitter) then begin
if (APart = zpNowhere)
or ((APart = zpSizer) and not HasSplitter)
or ((AStyle = hsNone) and (APart <> zpClient))
then begin
Result := Rect(0,0,0,0);
exit;
end;
Result := ARect;
if AStyle = hsNone then
exit; //client fills entire area
with HeaderPartMap[AStyle, APart] do begin
if AOrientation = doVertical then begin //portrait
//handle client w/o splitter
@ -324,6 +344,9 @@ var
begin
(* Some colors inavailable on some widgetsets!
*)
if AZone.Style = hsNone then
exit; //no header at all
IsMouseDown := (GetKeyState(VK_LBUTTON) and $80) <> 0;
//debug
AControl := AZone.FChildControl;