diff --git a/examples/dockmanager/easyedit/feditbook.pas b/examples/dockmanager/easyedit/feditbook.pas index 0d38aef3a2..a72cfedc7d 100644 --- a/examples/dockmanager/easyedit/feditbook.pas +++ b/examples/dockmanager/easyedit/feditbook.pas @@ -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 diff --git a/examples/dockmanager/elasticsite/feditorsite.lrs b/examples/dockmanager/elasticsite/feditorsite.lrs index 66d801e7b5..1e6e2ebcfe 100644 --- a/examples/dockmanager/elasticsite/feditorsite.lrs +++ b/examples/dockmanager/elasticsite/feditorsite.lrs @@ -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' diff --git a/examples/dockmanager/elasticsite/feditorsite.pas b/examples/dockmanager/elasticsite/feditorsite.pas index 5c55a6dcac..26ea18cffc 100644 --- a/examples/dockmanager/elasticsite/feditorsite.pas +++ b/examples/dockmanager/elasticsite/feditorsite.pas @@ -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; diff --git a/examples/dockmanager/package/easydocksite.pas b/examples/dockmanager/package/easydocksite.pas index 4d0cc303f4..ebd639ada6 100644 --- a/examples/dockmanager/package/easydocksite.pas +++ b/examples/dockmanager/package/easydocksite.pas @@ -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; diff --git a/examples/dockmanager/package/fdockbook.pas b/examples/dockmanager/package/fdockbook.pas index 41cc31f80d..253e76abab 100644 --- a/examples/dockmanager/package/fdockbook.pas +++ b/examples/dockmanager/package/fdockbook.pas @@ -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; diff --git a/examples/dockmanager/package/ffloatingsite.pas b/examples/dockmanager/package/ffloatingsite.pas index 2800014c65..7f28e6a54b 100644 --- a/examples/dockmanager/package/ffloatingsite.pas +++ b/examples/dockmanager/package/ffloatingsite.pas @@ -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 diff --git a/examples/dockmanager/package/zoneheader.inc b/examples/dockmanager/package/zoneheader.inc index 941a97b179..a957d25a49 100644 --- a/examples/dockmanager/package/zoneheader.inc +++ b/examples/dockmanager/package/zoneheader.inc @@ -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;