{%MainUnit ../extctrls.pp} {****************************************************************************** TControlBar ****************************************************************************** ***************************************************************************** This file is part of the Lazarus Component Library (LCL) See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** } { TCtrlBand } function TCtrlBand.GetBandRect: TRect; begin Result.Left := FLeft; Result.Top := FTop; Result.Right := Result.Left + FWidth; Result.Bottom := Result.Top + FHeight; end; function TCtrlBand.GetBottom: Integer; begin Result := Top + Height; end; function TCtrlBand.GetRight: Integer; begin Result := Left + Width; end; procedure TCtrlBand.SetBandRect(AValue: TRect); begin Left := AValue.Left; Top := AValue.Top; Width := AValue.Right - AValue.Left; Height := AValue.Bottom - AValue.Top; end; procedure TCtrlBand.SetRight(AValue: Integer); begin Left := AValue - Width; end; { TCtrlBands } function TCtrlBands.GetIndex(AControl: TControl): Integer; var i: Integer; begin Result := -1; for i := 0 to Count - 1 do if Items[i].Control = AControl then begin Result := i; break; end; end; { TCustomControlBar } constructor TCustomControlBar.Create(AOwner: TComponent); begin FBands := TCtrlBands.Create(True); inherited Create(AOwner); ControlStyle := [csAcceptsControls, csAutoSizeKeepChildLeft, csAutoSizeKeepChildTop, csCaptureMouse, csClickEvents, csDoubleClicks, csOpaque, csParentBackground]; FAutoDrag := True; FAutoDock := True; BevelOuter:=bvLowered; BevelInner:=bvRaised; DockSite := True; GradientDirection := gdVertical; GradientStartColor := clDefault; GradientEndColor := clDefault; FPicture := TPicture.Create; FPicture.OnChange := @PictureChanged; FRowSize := 26; FRowSnap := True; SetInitialBounds(0, 0, 100, 50); end; destructor TCustomControlBar.Destroy; begin FreeAndNil(FBands); FPicture.Free; inherited Destroy; end; procedure TCustomControlBar.AlignControlsToBands; var bR2L: Boolean; aBand: TCtrlBand; begin bR2L := IsRightToLeft; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomControlBar.AlignControlsToBands'){$ENDIF}; try for aBand in FVisiBands do AlignControlToBand(aBand, bR2L); finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomControlBar.AlignControlsToBands'){$ENDIF}; end; end; procedure TCustomControlBar.AlignControlToBand(ABand: TCtrlBand; ARightToLeft: Boolean); begin ABand.Control.Align:=alNone; ABand.Control.Top := ABand.Top + (ABand.Height - ABand.Control.Height) div 2; if not ARightToLeft then ABand.Control.Left := ABand.Left + cFullGrabber else ABand.Control.Left := ABand.Left + cBandBorderH; { store positions for compare in next Resize => efficient resizing } ABand.ControlLeft := ABand.Control.Left; ABand.ControlTop := ABand.Control.Top; ABand.ControlHeight := ABand.Control.Height; ABand.ControlWidth := ABand.Control.Width; end; procedure TCustomControlBar.BeginUpdate; begin inc(FUpdateCount); end; function TCustomControlBar.CalcBandHeight(AControl: TControl): Integer; begin if RowSnap then Result := CalcBandHeightSnapped(AControl) else Result := 2 * cBandBorderV + AControl.Height; end; function TCustomControlBar.CalcBandHeightSnapped(AControl: TControl): Integer; var rowsz, rowcnt: integer; begin rowsz := RowSize; rowcnt := (AControl.Height - (2 * cBandBorderV) + (rowsz-1)) div rowsz; Result := rowcnt * rowsz; end; function TCustomControlBar.CalcInnerBevelWidth: Integer; begin Result := 0; if BevelOuter <> bvNone then inc(Result, BevelWidth); if BevelInner <> bvNone then inc(Result, BevelWidth); inc(Result, BorderWidth); end; function TCustomControlBar.CalcLowestBandBottomPx: Integer; var aBand: TCtrlBand; begin Result := 0; for aBand in FVisiBands do Result := Math.max(Result, aBand.Bottom); end; procedure TCustomControlBar.CalculatePreferredSize(var PreferredWidth, PreferredHeight: Integer; WithThemeSpace: Boolean); begin //DebugLn('TCustomControlBar.CalculatePreferredSize'); CheckBandsSizeAndVisibility; PreferredWidth := 0; PreferredHeight := CalcLowestBandBottomPx; if PreferredHeight > 0 then inc(PreferredHeight, CalcInnerBevelWidth); end; procedure TCustomControlBar.ChangeCursor(ACursor: TCursorDesign); begin FCursorLock := True; case ACursor of cdDefault: Cursor := FDefCursor; cdGrabber: Cursor := crDrag; cdRestricted: Cursor := crNo; end; FCursorLock := False; end; procedure TCustomControlBar.CheckBandsSizeAndVisibility; var aBand: TCtrlBand; aIndent: Integer; bCtrlVisible: Boolean; begin if not (FBandMove = bmMoving) and not FLockResize then begin for aBand in FBands do begin bCtrlVisible := aBand.Control.Visible; if aBand.ControlVisible <> bCtrlVisible then begin if bCtrlVisible then begin InitializeBand(aBand, True); end else begin aBand.ControlVisible := bCtrlVisible; SortVisibleBands; end; break; end; end; SortVisibleBands; NormalizeRows; for aBand in FVisiBands do if (aBand.ControlLeft <> aBand.Control.Left) or (aBand.ControlTop <> aBand.Control.Top) or (aBand.ControlHeight <> aBand.Control.Height) or (aBand.ControlWidth <> aBand.Control.Width) then begin aBand.Width := cFullGrabber + aBand.Control.Width + cBandBorderH; aBand.Height := CalcBandHeightSnapped(aBand.Control); if not IsRightToLeft then aIndent := cFullGrabber else aIndent := cBandBorderH; InitializeMove(aBand); FInitDrag := Point(aBand.Left, aBand.Top); MoveBand(aBand, aBand.Control.Left - aIndent, aBand.Control.Top - (aBand.Height - aBand.Control.Height) div 2, False); break; end; end; end; procedure TCustomControlBar.CMBiDiModeChanged(var Message: TLMessage); var i, aWidth: Integer; begin inherited CMBiDiModeChanged(Message); aWidth := Width; DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomControlBar.CMBiDiModeChanged'){$ENDIF}; try for i := 0 to FBands.Count - 1 do FBands[i].Left := abs(FBands[i].Left - aWidth) - FBands[i].Width; finally EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomControlBar.CMBiDiModeChanged'){$ENDIF}; end; end; procedure TCustomControlBar.CMBorderChanged(var Message: TLMessage); var i, aNewBevelWidth, aShift: Integer; begin inherited CMBorderChanged(Message); if not (csLoading in ComponentState) then begin aNewBevelWidth := CalcInnerBevelWidth; if aNewBevelWidth <> FInnerBevelWidth then begin aShift := aNewBevelWidth - FInnerBevelWidth; for i := 0 to FBands.Count - 1 do FBands[i].Top := FBands[i].Top + aShift; if IsRightToLeft then aShift := - aShift; for i := 0 to FBands.Count - 1 do FBands[i].Left := FBands[i].Left + aShift; FInnerBevelWidth := aNewBevelWidth; AlignControlsToBands; end; end; end; procedure TCustomControlBar.CreateWnd; begin //DebugLn('TCustomControlBar.CreateWnd'); inherited CreateWnd; FDefCursor := Cursor; FPrevWidth := Width; end; procedure TCustomControlBar.DoBandMove(AControl: TControl; var ARect: TRect); begin //DebugLn('TCustomControlBar.DoBandMove'); if assigned(FOnBandMove) then FOnBandMove(self, AControl, ARect); end; procedure TCustomControlBar.DoBandPaint(AControl: TControl; ACanvas: TCanvas; var ARect: TRect; var AOptions: TBandPaintOptions); begin if assigned(FOnBandPaint) then FOnBandPaint(self, AControl, ACanvas, ARect, AOptions); end; function TCustomControlBar.DragControl(AControl: TControl; X, Y: Integer; KeepCapture: Boolean): Boolean; begin //DebugLn('TCustomControlBar.DragControl'); Result := True; if assigned(AControl) and assigned(FOnBandDrag) then FOnBandDrag(self, AControl, Result); if Result then DragManager.DragStart(AControl, True, -1, True); end; procedure TCustomControlBar.DragOver(Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean); begin inherited DragOver(Source, X, Y, State, Accept); Accept := Accept or (FBands.GetIndex(Source as TControl) < 0); end; procedure TCustomControlBar.EndUpdate; begin dec(FUpdateCount); if FUpdateCount = 0 then begin Invalidate; end; end; procedure TCustomControlBar.FlipChildren(AllLevels: Boolean); begin // Do nothing end; procedure TCustomControlBar.GetControlInfo(AControl: TControl; var Insets: TRect; var PreferredSize, RowCount: Integer); begin if assigned(FOnBandInfo) then FOnBandInfo(self, AControl, Insets, PreferredSize, RowCount); end; function TCustomControlBar.HitTest(X, Y: Integer): TControl; var i: Integer; aPoint: TPoint; aRect: TRect; begin Result := nil; aPoint := Point(X, Y); for i := 0 to length(FVisiBands) - 1 do begin aRect := FVisiBands[i].Control.ClientRect; if PtInRect(aRect, aPoint) then begin Result := FVisiBands[i].Control; break; end; end; end; class constructor TCustomControlBar.InitializeClass; begin cFullGrabber := cGrabWidth + 2 * cBandBorderH; end; procedure TCustomControlBar.InitializeBand(ABand: TCtrlBand; AKeepPos: Boolean); var i, j, k, aBevel, aCount, aLeft, aLimit, aRight, aTop: Integer; aRect: TRect; bR2L: Boolean; function GetOverlapBand: Integer; begin Result := 0; while Result < aCount do begin if IsBandOverlap(aRect, FVisiBands[Result].BandRect) then break; inc(Result); end; end; begin //DebugLn('TCustomControlBar.InitializeBand'); { control is not yet part of FVisiBands } aBevel := FInnerBevelWidth; aCount := length(FVisiBands); bR2L := IsRightToLeft; { calc. row for the new band } j := CalcLowestBandBottomPx; if j > 0 then j := (j - aBevel) div RowSize; { calc. initial geometry } ABand.Width := cFullGrabber + ABand.Control.Width + cBandBorderH; if not bR2L then begin aLeft := Math.max(ABand.Control.Left - cFullGrabber, aBevel); aLeft := Math.min(aLeft, ClientWidth - aBevel - cFullGrabber); end else begin aLeft := Math.min(ClientWidth - aBevel - ABand.Width, ABand.Control.Left - aBevel); aLeft := Math.max(aLeft, aBevel + cFullGrabber); end; aTop := aBevel + Math.max(Math.min(j, (ABand.Control.Top - aBevel) div RowSize), 0) * RowSize; ABand.Height := CalcBandHeight(ABand.Control); { check whether the new band overlap any other } aRect := Rect(aLeft, aTop, aLeft + ABand.Width, aTop + ABand.Height); i := GetOverlapBand; { try keep the ABand on its pos. and move others down } if (i < aCount) and AKeepPos then begin for k := 0 to aCount - 1 do FVisiBands[k].InitTop := FVisiBands[k].Top; j := ABand.Height; for i := 0 to aCount - 1 do begin if FVisiBands[i].Top >= aTop then FVisiBands[i].Top := FVisiBands[i].Top + j; end; i := GetOverlapBand; if i < aCount then for k := 0 to aCount - 1 do FVisiBands[k].Top := FVisiBands[k].InitTop; end; if i < aCount then begin { attempt to stick band by some existing band } k := 0; if not bR2L then begin aLimit := ClientWidth - aBevel - cFullGrabber; j := 0; if FVisiBands[i].Top <= aTop then j := i; while j < aCount do begin aRight := FVisiBands[j].Right; if (aLeft <= aRight) and (aRight <= aLimit) then begin aRect.Left := aRight; aRect.Right := aRect.Left + ABand.Width; aRect.Top := FVisiBands[j].Top; aRect.Bottom := aRect.Top + ABand.Height; k := GetOverlapBand; if k = aCount then break; end; inc(j); end; end else begin j := i; while (j < aCount) and (FVisiBands[j].Top = aTop) do begin aRect.Right := FVisiBands[j].Left; aRect.Left := aRect.Right - ABand.Width; k := GetOverlapBand; if k = aCount then break; inc(j); end; end; { attempt failed, place band below } if k < aCount then begin aRect.Left := aLeft; aRect.Right := aLeft + ABand.Width; aRect.Top := aTop + ABand.Height - RowSize; aRect.Bottom := aRect.Top + ABand.Height; while GetOverlapBand < aCount do begin inc(aRect.Top, RowSize); inc(aRect.Bottom, RowSize); end; end; DoBandMove(ABand.Control, aRect); end ; ABand.BandRect := aRect; ABand.ControlVisible := ABand.Control.Visible; AlignControlToBand(ABand, bR2L); end; procedure TCustomControlBar.InitializeMove(AMovingBand: TCtrlBand); var i: Integer; aBand: TCtrlBand; begin for aBand in FVisiBands do begin aBand.InitLeft := aBand.Left; aBand.InitTop := aBand.Top; end; { copy FVisiBands to FVisiBandsEx ommiting AMovingBand } SetLength(FVisiBandsEx, length(FVisiBands) - 1); i := 0; for aBand in FVisiBands do if aBand <> AMovingBand then begin FVisiBandsEx[i] := aBand; inc(i); end; end; procedure TCustomControlBar.InsertControl(AControl: TControl; Index: Integer); var aBand: TCtrlBand; aRect: TRect; aWidth, aRows: Integer; begin //DebugLn('TCustomControlBar.InsertControl'); inherited InsertControl(AControl, Index); if not (csLoading in ComponentState) then { new control is not yet in FVisiBands } if AControl is TWinControl then begin aBand := TCtrlBand.Create; AControl.Align := alNone; aBand.Control := AControl; InitializeBand(aBand, False); aRect := AControl.BoundsRect; aWidth := aBand.Width; aRows := aBand.Height div RowSize; GetControlInfo(AControl, aRect, aWidth, aRows); FBands.Add(aBand); end; end; function TCustomControlBar.IsBandOverlap(ARect, BRect: TRect): Boolean; begin Result := not ((ARect.Right <= BRect.Left) or (BRect.Right <= ARect.Left) or (ARect.Bottom <= BRect.Top) or (BRect.Bottom <= ARect.Top)); end; procedure TCustomControlBar.Loaded; var i, aIndent: Integer; aBand: TCtrlBand; begin inherited Loaded; if not IsRightToLeft then aIndent := cFullGrabber else aIndent := cBandBorderH; for i := 0 to ControlCount - 1 do begin aBand := TCtrlBand.Create; aBand.Control := Controls[i]; aBand.Height := CalcBandHeight(aBand.Control); aBand.Width := cFullGrabber + Controls[i].Width + cBandBorderH; aBand.Top := Controls[i].Top - (aBand.Height - Controls[i].Height) div 2; aBand.Left := Controls[i].Left - aIndent; FBands.Add(aBand); end; end; procedure TCustomControlBar.MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin //DebugLn('TCustomControlBar.MouseDown'); inherited MouseDown(Button, Shift, X, Y); if (Button = mbLeft) and (FBandMove = bmReady) then begin FBandMove := bmMoving; InitializeMove(FHoveredBand); FInitDrag := Point(X, Y); end; end; procedure TCustomControlBar.MouseMove(Shift: TShiftState; X, Y: Integer); var aBand: TCtrlBand; aCursor: TCursorDesign; bDragging, bGrabber: Boolean; begin inherited MouseMove(Shift, X, Y); if FBandMove <> bmMoving then begin aBand := MouseToBandPos(X, Y, bGrabber); if not bGrabber then aCursor := cdDefault else aCursor := cdGrabber; ChangeCursor(aCursor); if bGrabber then begin FBandMove := bmReady; FHoveredBand := aBand; end else begin FBandMove := bmNone; FHoveredBand := nil; end; end else begin if not DragManager.CanStartDragging(self,-1,X,Y) then begin MoveBand(FHoveredBand, X, Y, True); end else begin bDragging := False; if AutoDrag then bDragging := DragControl(FHoveredBand.Control, X, Y); if bDragging then FBandMove:=bmNone else MoveBand(FHoveredBand, X, Y, True); end; end; end; function TCustomControlBar.MouseToBandPos(X, Y: Integer; out AGrabber: Boolean): TCtrlBand; var aBand: TCtrlBand; aLeft, aTop: Integer; begin Result := nil; AGrabber := False; if length(FVisiBands) > 0 then begin if Y <= CalcLowestBandBottomPx then for aBand in FVisiBands do begin aLeft := aBand.Left; aTop := aBand.Top; if PtInRect(Rect(aLeft, aTop, aLeft + aBand.Width, aTop + aBand.Height), Point(X, Y)) then begin Result := aBand; if not IsRightToLeft then AGrabber := (X <= (aLeft + cFullGrabber)) else AGrabber := (X >= (aLeft + aBand.Width - cFullGrabber)); exit; { Exit! } end; end; end; end; procedure TCustomControlBar.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); begin //DebugLn('TCustomControlBar.MouseUp'); inherited MouseUp(Button, Shift, X, Y); if FBandMove = bmMoving then begin FBandMove := bmNone; ChangeCursor(cdDefault); SortVisibleBands; end; end; procedure TCustomControlBar.MoveBand(AMoveBand: TCtrlBand; X, Y: Integer; ByMouse: Boolean); var i, aBevel, aCount, aLimit, aNewBound, aNewRow: Integer; aRect: TRect; bR2L, bRestrictCursor: Boolean; function IsNewPositionFree: Boolean; var k: Integer; begin k := 0; while k < aCount do begin if IsBandOverlap(aRect, FVisiBandsEx[k].BandRect) then break; inc(k); end; Result := (k = aCount); end; begin aBevel := FInnerBevelWidth; aCount := length(FVisiBandsEx); for i := 0 to aCount - 1 do begin FVisiBandsEx[i].Left := FVisiBandsEx[i].InitLeft; FVisiBandsEx[i].Top := FVisiBandsEx[i].InitTop; end; bR2L := IsRightToLeft; aNewRow := (Y - (FInitDrag.Y - AMoveBand.InitTop)) div RowSize; bRestrictCursor := (Y < aBevel) or (Y > (ClientHeight - aBevel)); if not bR2L then begin aLimit := ClientWidth - aBevel - cFullGrabber; aNewBound := X - (FInitDrag.X - AMoveBand.InitLeft); if aNewBound > aLimit then begin aNewBound := aLimit; bRestrictCursor := True; end; if aNewBound < aBevel then begin aNewBound := aBevel; bRestrictCursor := True; end; end else begin aLimit := ClientWidth - aBevel - AMoveBand.Width; aNewBound := X - (FInitDrag.X - AMoveBand.InitLeft); if aNewBound > aLimit then begin aNewBound := aLimit; bRestrictCursor := True; end; aLimit := aBevel + cFullGrabber - AMoveBand.Width; if aNewBound < aLimit then begin aNewBound := aLimit; bRestrictCursor := True; end; end; aRect := Rect(aNewBound, aNewRow * RowSize + aBevel, aNewBound + AMoveBand.Width, aNewRow * RowSize + aBevel + AMoveBand.Height); i := 0; while i < aCount do begin if IsBandOverlap(aRect, FVisiBandsEx[i].BandRect) then begin { attempts to stick band } if FVisiBandsEx[i].Left < aRect.Left then begin { band dragged from the right } aRect.Left := FVisiBandsEx[i].InitLeft + FVisiBandsEx[i].Width; aRect.Right := aRect.Left + AMoveBand.Width; if (not bR2L and (aRect.Left < (ClientWidth - aBevel - cFullGrabber))) or (bR2L and (aRect.Right <= (ClientWidth - aBevel))) then if IsNewPositionFree then i := aCount; break; end else { band dragged from the left } if (aRect.Left + AMoveBand.Width) > FVisiBandsEx[i].InitLeft then begin aRect.Right := FVisiBandsEx[i].Left; aRect.Left := aRect.Right - AMoveBand.Width; if (not bR2L and (aRect.Left >= aBevel)) or (bR2L and (aRect.Right > (aBevel + cFullGrabber))) then if IsNewPositionFree then i := aCount; break; end; end; inc(i); end; if i < aCount then begin { new place is occupied, fallback to the last valid pos. } bRestrictCursor := True; aRect.Left := AMoveBand.Left; aRect.Right := aRect.Left + AMoveBand.Width; aRect.Top := AMoveBand.Top; aRect.Bottom := aRect.Top + AMoveBand.Height; { check if the fallback pos is occupied, when band was resized } if not IsNewPositionFree then begin aRect.Top := CalcLowestBandBottomPx; aRect.Bottom := aRect.Top + AMoveBand.Height; end; end; if ByMouse then if not bRestrictCursor then ChangeCursor(cdGrabber) else ChangeCursor(cdRestricted); DoBandMove(AMoveBand.Control, aRect); if not EqualRect(aRect, AMoveBand.BandRect) then begin AMoveBand.BandRect := aRect; NormalizeRows; AlignControlsToBands; Invalidate; end; end; procedure TCustomControlBar.NormalizeRows; var i, j, aBevel, aCount, aMax: Integer; aRows: TBooleanDynArray; begin //DebugLn('TCustomControlBar.NormalizeRows'); { FVisiBands is not sorted here ! } aCount := length(FVisiBands); if aCount > 0 then begin aBevel := FInnerBevelWidth; { shift all rows so that the lowest begin at the top } j := high(Integer); aMax := 0; for i := 0 to aCount -1 do begin j := Math.min(j, FVisiBands[i].Top); aMax := Math.max(aMax, FVisiBands[i].Bottom); end; j := aBevel - j; if j <> 0 then begin for i := 0 to aCount - 1 do FVisiBands[i].Top := FVisiBands[i].Top + j; inc(aMax, j); end; { remove empty rows } aMax := (aMax - aBevel) div RowSize; SetLength(aRows, aMax); for i := 0 to aMax -1 do aRows[i] := False; for i := 0 to aCount - 1 do begin for j := ((FVisiBands[i].Top - aBevel) div RowSize) to ((FVisiBands[i].Bottom - aBevel) div RowSize) - 1 do aRows[j] := True; end; for i := aMax - 1 downto 0 do if not aRows[i] then begin for j := aCount - 1 downto 0 do if FVisiBands[j].Top > (i * RowSize + aBevel) then FVisiBands[j].Top := FVisiBands[j].Top - RowSize; end; end; end; procedure TCustomControlBar.Paint; const cBandBevel = 1; cOptions = [bpoGrabber, bpoFrame, bpoGradient, bpoRoundRect]; var aBevel: Integer; i, j: Integer; aOptions: TBandPaintOptions; aRect: TRect; aStartColor, aEndColor: TColor; begin inherited Paint; aBevel := CalcInnerBevelWidth; Canvas.Clipping := True; aRect := ClientRect; InflateRect(aRect, -aBevel, -aBevel); Canvas.ClipRect := aRect; if assigned(Picture) and (Picture.Width > 0) and (Picture.Height > 0) then begin for i := 0 to (ClientWidth - 2 * aBevel) div Picture.Width do for j := 0 to (ClientHeight - 2 * aBevel) div Picture.Height do Canvas.Draw(i * Picture.Width + aBevel, j * Picture.Height + aBevel, Picture.Bitmap); end; for i := 0 to length(FVisiBands) - 1 do begin aRect.Left := FVisiBands[i].Left; aRect.Top := FVisiBands[i].Top; aRect.Right := aRect.Left + FVisiBands[i].Width; aRect.Bottom := aRect.Top + FVisiBands[i].Height; aOptions := cOptions; DoBandPaint(FVisiBands[i].Control, Canvas, aRect, aOptions); if bpoFrame in aOptions then Canvas.Frame3d(aRect, cBandBevel, bvRaised); { Frame3D inflates aRect } if (bpoGradient in aOptions) and (DrawingStyle = dsGradient) then begin aStartColor := GradientStartColor; if aStartColor = clDefault then aStartColor := clForm; aEndColor := GradientEndColor; if aEndColor = clDefault then aEndColor := clHighlight; Canvas.GradientFill(aRect, aStartColor, aEndColor, GradientDirection); end; if bpoGrabber in aOptions then begin if not IsRightToLeft then begin inc(aRect.Left, cBandBorderH - cBandBevel); aRect.Right := aRect.Left + cGrabWidth; end else begin dec(aRect.Right, cBandBorderH - cBandBevel); aRect.Left := aRect.Right - cGrabWidth; end; inc(aRect.Top, cBandBevel); dec(aRect.Bottom, cBandBevel); Canvas.Brush.Style := bsClear; Canvas.Frame3D(aRect, cBandBevel, bvRaised); end; end; Canvas.Clipping := False; end; procedure TCustomControlBar.PictureChanged(Sender: TObject); begin if FUpdateCount = 0 then Invalidate; end; procedure TCustomControlBar.RemoveControl(AControl: TControl); var aIndex: Integer; begin //DebugLn('TCustomControlBar.RemoveControl', AControl.Name); aIndex := FBands.GetIndex(AControl); if aIndex >= 0 then FBands.Delete(aIndex); inherited RemoveControl(AControl); if not (csDestroying in ComponentState) then Invalidate; end; procedure TCustomControlBar.Resize; begin //DebugLn('TCustomControlBar.Resize'); inherited Resize; if not AutoSize then CheckBandsSizeAndVisibility; AlignControlsToBands; Invalidate; end; procedure TCustomControlBar.SetCursor(Value: TCursor); begin inherited SetCursor(Value); if not FCursorLock then FDefCursor := Value; end; procedure TCustomControlBar.SetDrawingStyle(AValue: TBandDrawingStyle); begin if FDrawingStyle = AValue then exit; FDrawingStyle := AValue; if FUpdateCount = 0 then Invalidate; end; procedure TCustomControlBar.SetGradientDirection(AValue: TGradientDirection); begin if FGradientDirection = AValue then exit; FGradientDirection := AValue; if FUpdateCount = 0 then Invalidate; end; procedure TCustomControlBar.SetGradientEndColor(AValue: TColor); begin if FGradientEndColor = AValue then exit; FGradientEndColor := AValue; if FUpdateCount = 0 then Invalidate; end; procedure TCustomControlBar.SetGradientStartColor(AValue: TColor); begin if FGradientStartColor = AValue then exit; FGradientStartColor := AValue; if FUpdateCount = 0 then Invalidate; end; procedure TCustomControlBar.SetPicture(aValue: TPicture); begin FPicture.Assign(aValue); end; procedure TCustomControlBar.SetRowSize(AValue: TRowSize); var aBand: TCtrlBand; aBevel, aRow, aOldRowSize: Integer; begin aOldRowSize := FRowSize; if aOldRowSize = AValue then exit; FRowSize := AValue; aBevel := FInnerBevelWidth; for aBand in FBands do begin aRow := (aBand.Top - aBevel) div aOldRowSize; aBand.Top := aRow * AValue + aBevel; end; if RowSnap then begin for aBand in FBands do aBand.Height := CalcBandHeightSnapped(aBand.Control); end; NormalizeRows; FLockResize := True; AlignControlsToBands; FLockResize := False; if AutoSize then InvalidatePreferredSize; if FUpdateCount = 0 then Invalidate; end; procedure TCustomControlBar.ShiftBands(AFrom, ATo, AShift, ALimit: Integer); var i: Integer; begin if not IsRightToLeft then begin for i := AFrom to ATo do if FVisiBands[i].Left >= ALimit then FVisiBands[i].Left := FVisiBands[i].Left + AShift end else begin for i := AFrom to ATo do if (FVisiBands[i].Left + FVisiBands[i].Width) <= ALimit then FVisiBands[i].Left := FVisiBands[i].Left - AShift; end; end; procedure TCustomControlBar.SortVisibleBands; var i, j, aCount: Integer; b: Boolean; aBand: TCtrlBand; begin //DebugLn('TCustomControlBar.SortVisiBands'); { calculate number of visible controls and set visibility of bands } aCount := 0; for i := 0 to FBands.Count - 1 do begin b := FBands[i].Control.Visible; FBands[i].Visible := b; if b then inc(aCount); end; { set length of FVisiBands } SetLength(FVisiBands, aCount); { assign visible bands to FVisiBands } j := 0; for i := 0 to FBands.Count - 1 do if FBands[i].Visible then begin FVisiBands[j] := FBands[i]; inc(j); end; { sort FVisiBands (when it makes sense) } if aCount > 1 then for j := 0 to aCount - 2 do for i := aCount - 1 downto j + 1 do if (FVisiBands[i].FTop < FVisiBands[i - 1].FTop) or ((FVisiBands[i].FTop = FVisiBands[i - 1].FTop) and (FVisiBands[i].FLeft < FVisiBands[i - 1].FLeft)) then begin aBand := FVisiBands[i]; FVisiBands[i] := FVisiBands[i - 1]; FVisiBands[i - 1] := aBand; end; end; procedure TCustomControlBar.StickControls; begin // ToDo AlignControlsToBands; end; procedure TCustomControlBar.WMSize(var Message: TLMSize); var i, aBevel, aBound, aCount, aLeftMost, aMove, aRightMost, aShift: Integer; bR2L: Boolean; begin //DebugLn('TCustomControlBar.WMSize'); inherited WMSize(Message); bR2L := IsRightToLeft; aCount := length(FVisiBands); aShift := FPrevWidth - Message.Width; if bR2L then for i := 0 to aCount - 1 do FVisiBands[i].Left := FVisiBands[i].Left - aShift; if aShift > 0 then begin if aCount > 0 then begin aBevel := FInnerBevelWidth; aBound := Message.Width - aBevel; aLeftMost := high(Integer); aRightMost := low(Integer); for i := 0 to aCount - 1 do begin aLeftMost := Math.min(aLeftMost, FVisiBands[i].Left); aRightMost := Math.max(aRightMost, FVisiBands[i].Right); end; end else begin aBevel := 0; aBound := 0; aLeftMost := 0; aRightMost := 0; end; if not bR2L then begin if (aRightMost > aBound) and (aLeftMost > aBevel) then begin aMove := Math.min(aLeftMost - aBevel, aRightMost - aBound); aMove := Math.min(aMove, aShift); for i := 0 to aCount - 1 do FVisiBands[i].Left := FVisiBands[i].Left - aMove; end; end else begin if (aLeftMost < aBevel) and (aRightMost < aBound) then begin aMove := Math.min(aBevel - aLeftMost, aBound - aRightMost); aMove := Math.min(aMove, aShift); for i := 0 to aCount - 1 do FVisiBands[i].Left := FVisiBands[i].Left + aMove; end; end; end; FPrevWidth := Message.Width; end;