mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-08-17 17:59:16 +02:00
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:
parent
4d743bce3e
commit
b76ca7675c
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user