From a75cc9ff3c4debb0a70637a32f352f592b6c31a4 Mon Sep 17 00:00:00 2001 From: paul Date: Sat, 20 Oct 2007 08:43:21 +0000 Subject: [PATCH] CheckGroupBox autosizing patch from Mattias git-svn-id: trunk@12530 - --- lcl/controls.pp | 27 ++++- lcl/include/wincontrol.inc | 241 ++++++++++++++++++++++++++----------- 2 files changed, 192 insertions(+), 76 deletions(-) diff --git a/lcl/controls.pp b/lcl/controls.pp index ebeb4afe02..1da9f4e46e 100644 --- a/lcl/controls.pp +++ b/lcl/controls.pp @@ -2933,6 +2933,10 @@ begin FShrinkVertical:=SrcSizing.ShrinkVertical; FControlsPerLine:=SrcSizing.ControlsPerLine; FLayout:=SrcSizing.Layout; + FLeftRightSpacing:=SrcSizing.LeftRightSpacing; + FTopBottomSpacing:=SrcSizing.TopBottomSpacing; + FHorizontalSpacing:=SrcSizing.HorizontalSpacing; + FVerticalSpacing:=SrcSizing.VerticalSpacing; Change; end else @@ -2955,20 +2959,30 @@ begin and (FShrinkHorizontal=Sizing.ShrinkHorizontal) and (FShrinkVertical=Sizing.ShrinkVertical) and (FControlsPerLine=Sizing.ControlsPerLine) - and (FLayout=Sizing.Layout); + and (FLayout=Sizing.Layout) + and (FLeftRightSpacing=Sizing.LeftRightSpacing) + and (FTopBottomSpacing=Sizing.TopBottomSpacing) + and (FHorizontalSpacing=Sizing.HorizontalSpacing) + and (FVerticalSpacing=Sizing.VerticalSpacing); end; procedure TControlChildSizing.SetGridSpacing(Spacing: integer); begin - LeftRightSpacing:=Spacing; - TopBottomSpacing:=Spacing; - HorizontalSpacing:=Spacing; - VerticalSpacing:=Spacing; + if (LeftRightSpacing=Spacing) + and (TopBottomSpacing=Spacing) + and (HorizontalSpacing=Spacing) + and (VerticalSpacing=Spacing) then exit; + fLeftRightSpacing:=Spacing; + fTopBottomSpacing:=Spacing; + fHorizontalSpacing:=Spacing; + fVerticalSpacing:=Spacing; + Change; end; procedure TControlChildSizing.Change; begin - Control.DoChildSizingChange(Self); + if Control<>nil then + Control.DoChildSizingChange(Self); if Assigned(FOnChange) then FOnChange(Self); end; @@ -3301,3 +3315,4 @@ end. + diff --git a/lcl/include/wincontrol.inc b/lcl/include/wincontrol.inc index 67a5394a5a..b1b13d240b 100644 --- a/lcl/include/wincontrol.inc +++ b/lcl/include/wincontrol.inc @@ -73,7 +73,8 @@ type Index: array[TAutoSizeBoxOrientation] of Integer; // index in parent or grandparent ChildCount: array[TAutoSizeBoxOrientation] of Integer; Childs: array[TAutoSizeBoxOrientation] of PAutoSizeBox; - + NewControlBounds: TRect; + // for nodes destructor Destroy; override; procedure Clear; @@ -98,13 +99,16 @@ type procedure SumTable; procedure ResizeTable(ChildSizing: TControlChildSizing; TargetWidth, TargetHeight: integer); - function SetTableControlBounds(ChildSizing: TControlChildSizing): boolean;// true if changed + procedure ComputeTableControlBounds(ChildSizing: TControlChildSizing); + function SetTableControlBounds(ChildSizing: TControlChildSizing + ): boolean;// true if changed function AlignControlsInTable(ListOfControls: TFPList; ChildSizing: TControlChildSizing; - TargetWidth, TargetHeight: integer): boolean;// true if changed + TargetWidth, TargetHeight: integer; + Apply: boolean): boolean;// true if changed // debugging - procedure WriteDebugReport; + procedure WriteDebugReport(const Title: string); end; { TAutoSizeCtrlData @@ -148,11 +152,12 @@ type private FChilds: TAvgLvlTree;// tree of TAutoSizeCtrlData function GetChilds(AControl: TControl): TAutoSizeCtrlData; + procedure DoMoveNonAlignedChilds(Side: TAnchorKind; + var MoveDiff: integer; FindMinimum: boolean); + procedure SetupNonAlignedChilds(MoveNonAlignedChilds: boolean); procedure AlignChilds; procedure SetupSpace; function ComputePositions: boolean;// false if recomputation is needed (a property changed) - procedure DoMoveNonAlignedChilds(Side: TAnchorKind; - var MoveDiff: integer; FindMinimum: boolean); public Control: TControl; // the Control of a leaf node WinControl: TWinControl;// the Control as TWinControl (else nil) @@ -169,6 +174,8 @@ type out PreferredClientWidth, PreferredClientHeight: integer); procedure FixControlProperties(Child: TControl); procedure ClearSides; + procedure SetFixedLeftTop(ChildData: TAutoSizeCtrlData; Side: TAnchorKind; + NewLeftTop: integer); property Childs[AControl: TControl]: TAutoSizeCtrlData read GetChilds; default; procedure WriteDebugReport(const Title, Prefix: string); end; @@ -309,7 +316,7 @@ begin for a:=Low(TAnchorKind) to High(TAnchorKind) do begin if ChildData.Sides[a].CtrlData=Self then begin // aligned or anchored to parent - if a in [akLeft,akTop] then begin + if a in [akLeft,akRight] then begin ChildData.Sides[a].Space:=Max(WinControl.ChildSizing.LeftRightSpacing, ChildData.Borders[a]); end else begin @@ -354,13 +361,16 @@ begin // dangling side if a in [akLeft,akTop] then begin ChildData.Sides[a].Side:=asrRight; - ChildData.Sides[a].Space:= - Max(WinControl.ChildSizing.HorizontalSpacing, - ChildData.Borders[a]); end else begin ChildData.Sides[a].Side:=asrLeft; + end; + if a in [akLeft,akRight] then begin ChildData.Sides[a].Space:= - Max(WinControl.ChildSizing.VerticalSpacing, + Max(WinControl.ChildSizing.LeftRightSpacing, + ChildData.Borders[a]); + end else begin + ChildData.Sides[a].Space:= + Max(WinControl.ChildSizing.TopBottomSpacing, ChildData.Borders[a]); end; end; @@ -707,39 +717,118 @@ begin MoveDiffValid:=true; end; end else begin - ChildData.Sides[Side].CtrlData:=Self; - ChildData.Sides[Side].Side:=asrLeft; - ChildData.Sides[Side].Space:=Child.GetSidePosition(Side)-MoveDiff; - ChildData.Sides[Side].Distance[assddLeftTop]:=ChildData.Sides[Side].Space; - ChildData.Sides[Side].DistanceState[assddLeftTop]:=assdfValid; + SetFixedLeftTop(ChildData,Side,Child.GetSidePosition(Side)-MoveDiff); end; end; end; end; +procedure TAutoSizeCtrlData.SetupNonAlignedChilds(MoveNonAlignedChilds: boolean); +var + ChildSizing: TControlChildSizing; + Box: TAutoSizeBox; + y: Integer; + RowBox: TAutoSizeBox; + x: Integer; + ControlBox: TAutoSizeBox; + Child: TControl; + NewBounds: TRect; + ChildData: TAutoSizeCtrlData; + MoveDiff: Integer; + AlignList: TFPList; +begin + if ChildCount=0 then exit; + if WinControl.ChildSizing.Layout=cclNone then begin + // move the non-aligned controls (i.e. not aligned or fixed anchored) + // Find the leftmost and topmost of those controls + MoveDiff:=0; + DoMoveNonAlignedChilds(akLeft,MoveDiff,true); + //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea akLeft MoveDiff=',MoveDiff]); + if not MoveNonAlignedChilds then MoveDiff:=0; + DoMoveNonAlignedChilds(akLeft,MoveDiff,false); + MoveDiff:=0; + DoMoveNonAlignedChilds(akTop,MoveDiff,true); + //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea akTop MoveDiff=',MoveDiff]); + if not MoveNonAlignedChilds then MoveDiff:=0; + DoMoveNonAlignedChilds(akTop,MoveDiff,false); + end else begin + // there is an automatic layout for non aligned childs + // use the layout engine, but with static values + ChildSizing:=nil; + Box:=nil; + AlignList:=TFPList.Create; + try + WinControl.CreateControlAlignList(alNone,AlignList,nil); + if AlignList.Count=0 then exit; + ChildSizing:=TControlChildSizing.Create(nil); + Box:=TAutoSizeBox.Create; + // copy current ChildSizing ... + ChildSizing.Assign(WinControl.ChildSizing); + // ... and change it to static layout (i.e. independent of parent size) + ChildSizing.ShrinkHorizontal:=crsAnchorAligning; + ChildSizing.EnlargeHorizontal:=crsAnchorAligning; + ChildSizing.ShrinkVertical:=crsAnchorAligning; + ChildSizing.EnlargeVertical:=crsAnchorAligning; + // compute static layout + Box.AlignControlsInTable(AlignList,ChildSizing, + WinControl.ClientWidth,WinControl.ClientHeight,false); + //Box.WriteDebugReport('TAutoSizeCtrlData.SetupNonAlignedChilds'); + // transfer the coords of the layout + for y:=0 to Box.ChildCount[asboVertical]-1 do begin + RowBox:=Box.Childs[asboVertical][y]; + for x:=0 to RowBox.ChildCount[asboHorizontal]-1 do begin + ControlBox:=RowBox.Childs[asboHorizontal][x]; + Child:=ControlBox.Control; + if Child=nil then continue; + NewBounds:=ControlBox.NewControlBounds; + //DebugLn(['TAutoSizeCtrlData.SetupNonAlignedChilds ',DbgSName(Child),' ',dbgs(NewBounds)]); + ChildData:=Childs[Child]; + // set left + SetFixedLeftTop(ChildData,akLeft,NewBounds.Left); + // set width + ChildData.PreferredSize[asboHorizontal]:=NewBounds.Right-NewBounds.Left; + // set top + SetFixedLeftTop(ChildData,akTop,NewBounds.Top); + // set height + ChildData.PreferredSize[asboVertical]:=NewBounds.Bottom-NewBounds.Top; + end; + end; + finally + ChildSizing.Free; + Box.Free; + AlignList.Free; + end; + end; +end; + procedure TAutoSizeCtrlData.ComputePreferredClientArea( MoveNonAlignedChilds: boolean; out PreferredClientWidth, PreferredClientHeight: integer); -{ if MoveNonAlignedChilds=true then all non-aligned childs will be moved the +{ if MoveNonAlignedChilds=true then all non-aligned childs will be moved in parallel, so that at least one child is positioned left most and one child is positioned top most. Type of controls: - 1. non-aligned: the left side of the control has only a designed position. + 1. layout: the left and top side of the control has only designed position + and Parent.ChildSizing.Layout <> cclNone. + That means: Align=alNone, Anchors=[akLeft,akTop], + AnchorSide[akLeft/akTop].Control=nil, Parent.ChildSizing.Layout <> cclNone + 2. non-aligned: the left+top side of the control has only a designed position. That means: Align=alNone, akLeft is set, AnchorSide[akLeft].Control=nil and Parent.ChildSizing.Layout=cclNone - 2. Aligned: Align<>alNone + Same for akTop. + 3. Aligned: Align<>alNone These are put consecutively into the remaining space. BorderSpacing and AdjustClientRect defines the space. The aligned sides automatically set the Anchors and the AnchorSide.Control to nil. alLeft,alRight,alTop,alBottom have one free side, which can be anchored. - 3. centered: akLeft and akRight are not set - 4. one side anchored: akLeft is set and akRight is not + 4. centered: akLeft and akRight are not set + 5. one side anchored: akLeft is set and akRight is not OR akRight is set and akLeft is not - 4.1 anchored to a side (asrLeft,asrRight) - 4.2 anchored to a center (asrCenter) - 5. both sides anchored: akLeft and akRight not + 5.1 anchored to a side (asrLeft,asrRight) + 5.2 anchored to a center (asrCenter) + 6. both sides anchored: akLeft and akRight not Note: asrCenter is not allowed here Circles and invalid combinations will be automatically fixed. @@ -829,7 +918,6 @@ var VisibleCount: Integer; Child: TControl; ChildData: TAutoSizeCtrlData; - MoveDiff: Integer; a: TAnchorKind; CurNeededClientWH: Integer; begin @@ -837,16 +925,7 @@ begin PreferredClientHeight:=0; if ChildCount=0 then exit; - if (WinControl.ChildSizing.Layout<>cclNone) - and ((WinControl.ChildSizing.EnlargeHorizontal<>crsAnchorAligning) - or (WinControl.ChildSizing.ShrinkHorizontal<>crsAnchorAligning) - or (WinControl.ChildSizing.EnlargeVertical<>crsAnchorAligning) - or (WinControl.ChildSizing.ShrinkVertical<>crsAnchorAligning)) - then begin - // dynamic layouts are not yet supported - exit; - end; - + // fix control properties // check if there are visible childs VisibleCount:=0; @@ -890,19 +969,7 @@ begin end; end; - // First move the non-aligned controls (i.e. not aligned or fixed anchored) - // Find the leftmost and topmost of those controls - MoveDiff:=0; - DoMoveNonAlignedChilds(akLeft,MoveDiff,true); - //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea akLeft MoveDiff=',MoveDiff]); - if not MoveNonAlignedChilds then MoveDiff:=0; - DoMoveNonAlignedChilds(akLeft,MoveDiff,false); - MoveDiff:=0; - DoMoveNonAlignedChilds(akTop,MoveDiff,true); - //DebugLn(['TAutoSizeCtrlData.ComputePreferredClientArea akTop MoveDiff=',MoveDiff]); - if not MoveNonAlignedChilds then MoveDiff:=0; - DoMoveNonAlignedChilds(akTop,MoveDiff,false); - + SetupNonAlignedChilds(MoveNonAlignedChilds); // setup the dependencies for Aligned controls AlignChilds; @@ -1022,6 +1089,16 @@ begin end; end; +procedure TAutoSizeCtrlData.SetFixedLeftTop(ChildData: TAutoSizeCtrlData; + Side: TAnchorKind; NewLeftTop: integer); +begin + ChildData.Sides[Side].CtrlData:=Self; + ChildData.Sides[Side].Side:=asrLeft; + ChildData.Sides[Side].Space:=NewLeftTop; + ChildData.Sides[Side].Distance[assddLeftTop]:=NewLeftTop; + ChildData.Sides[Side].DistanceState[assddLeftTop]:=assdfValid; +end; + procedure TAutoSizeCtrlData.WriteDebugReport(const Title, Prefix: string); function GetDistance(a: TAnchorKind; d: TAutoSizeSideDistDirection): string; @@ -1080,6 +1157,7 @@ begin false, // with constraints false // without theme space ); + //DebugLn(['TAutoSizeBox.SetControl ',DbgSName(Control),' ',PreferredSize[asboHorizontal]]); Control.BorderSpacing.GetSpaceAround(Border); BorderLeftTop[asboHorizontal]:=Border.Left; BorderLeftTop[asboVertical]:=Border.Top; @@ -1722,13 +1800,13 @@ begin //DebugLn('TAutoSizeBox.ResizeChilds CurSize=',dbgs(CurSize),' TargetSize=',dbgs(TargetSize)); EnlargeStyle:=crsAnchorAligning; ShrinkStyle:=crsAnchorAligning; + i:=0; if TargetSize>CurSize then begin // enlarge if Orientation=asboHorizontal then EnlargeStyle:=ChildSizing.EnlargeHorizontal else EnlargeStyle:=ChildSizing.EnlargeVertical; - i:=0; while TargetSize>CurSize do begin // shrink childs GetChildMaxResize(MaxResizeFactorPerItem,ResizeableCount); @@ -1755,7 +1833,6 @@ begin ShrinkStyle:=ChildSizing.ShrinkHorizontal else ShrinkStyle:=ChildSizing.ShrinkVertical; - i:=0; while TargetSizecclNone) - and ((ChildSizing.EnlargeHorizontal<>crsAnchorAligning) - or (ChildSizing.ShrinkHorizontal<>crsAnchorAligning)); + Result:=false; end; function WidthDependsOnParent: boolean; @@ -2783,10 +2887,7 @@ var function HeightDependsOnChilds: boolean; begin - Result:=HasVisibleChilds - and (ChildSizing.Layout<>cclNone) - and ((ChildSizing.EnlargeVertical<>crsAnchorAligning) - or (ChildSizing.ShrinkVertical<>crsAnchorAligning)); + Result:=false; end; function HeightDependsOnParent: boolean; @@ -5296,7 +5397,7 @@ begin Box:=TAutoSizeBox.Create; try BoundsModified:=Box.AlignControlsInTable(ListOfControls,ChildSizing, - ClientWidth,ClientHeight); + ClientWidth,ClientHeight,true); finally Box.Free; end;