lazarus/lcl/include/customsplitter.inc
2023-07-03 06:23:49 +03:00

1011 lines
30 KiB
PHP

{%MainUnit ../extctrls.pp}
{******************************************************************************
TCustomSplitter
******************************************************************************
*****************************************************************************
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.
*****************************************************************************
}
function FindOppositeControl(Control: TControl): TControl;
var
i,count: Integer;
CurControl: TControl;
fRect: TRect;
fPoint: TPoint;
alignList: TAlignSet;
begin
Result := nil;
count := Control.Parent.ControlCount;
if count > 0 then
begin
fRect := Control.BoundsRect;
case Control.Align of
alTop: begin
fPoint := fRect.BottomRight;
fPoint.y := fPoint.y+1;
Dec(fPoint.x);
alignList := [alTop,alRight,alClient];
end;
alBottom: begin
fPoint := fRect.TopLeft;
Dec(fPoint.y);
alignList := [alLeft,alBottom,alClient];
end;
alLeft: begin
fPoint := fRect.BottomRight;
Inc(fPoint.x);
Dec(fPoint.y);
alignList := [alLeft,alClient];
end;
alRight: begin
fPoint := fRect.TopLeft;
Dec(fPoint.x);
alignList := [alRight,alClient];
end;
end; // case
Dec(count);
for i := 0 to count do
begin
CurControl := Control.Parent.Controls[i];
if (CurControl <> Control) and
CurControl.Visible and
(CurControl.Align in alignList) and
(PtInRect(CurControl.BoundsRect, fPoint)) then Result := CurControl;
if Assigned(Result) then Break;
end; // for i
end;
end;
function FindVirtualOppositeControl(Control: TControl): TControl;
function CompPos(CurControl, Control: TControl): Boolean;
begin
Result := False;
case Control.Align of
alTop:
if (CurControl.Align = Control.Align) and
(CurControl.Top >= Control.BoundsRect.Bottom) then Result := True;
alBottom:
if (CurControl.Align = Control.Align) and
(CurControl.BoundsRect.Bottom <= Control.Top) then Result := True;
alLeft:
if (CurControl.Align = Control.Align) and
(CurControl.Left >= Control.BoundsRect.Right) then Result := True;
alRight:
if (CurControl.Align = Control.Align) and
(CurControl.BoundsRect.Right <= Control.Left) then Result := True;
end;
end;
function OppositeControl(CurControl,Control: TControl): Boolean;
begin
Result := False;
case Control.Align of
alLeft: if (CurControl.Align = alRight) then Result := True;
alRight: if (CurControl.Align = alLeft) then Result := True;
alTop: if (CurControl.Align = alBottom) then Result := True;
alBottom: if (CurControl.Align = alTop) then Result := True;
end;
end;
var
i,count: Integer;
CurControl: TControl;
begin
Result := nil;
count := Control.Parent.ControlCount;
if count > 0 then
begin
Dec(count);
for i := 0 to count do
begin
CurControl := Control.Parent.Controls[i];
if (CurControl <> Control) then
begin
if ((Result = nil) and OppositeControl(CurControl, Control)) or
(Assigned(Result) and CompPos(CurControl, Result)) then
Result := CurControl;
end;
end; // for i
end;
end;
{ TCustomSplitter }
class procedure TCustomSplitter.WSRegisterClass;
begin
inherited WSRegisterClass;
RegisterCustomSplitter;
end;
function TCustomSplitter.GetResizeControl: TControl;
begin
if Align in [alLeft,alRight,alTop,alBottom] then
Result := FindAlignControl
else
Result := AnchorSide[ResizeAnchor].Control;
end;
function TCustomSplitter.GetOtherResizeControl: TControl;
begin
if Align in [alLeft, alRight, alTop, alBottom] then
Result := FindAlignOtherControl
else
Result := AnchorSide[OppositeAnchor[ResizeAnchor]].Control;
end;
procedure TCustomSplitter.MoveSplitter(Offset: integer);
var
CurResizeControl,
LastControl, VirtualOppositeControl, CurOtherResizeControl: TControl;
function GetParentClientSize: Integer;
begin
case ResizeAnchor of
akLeft, akRight: Result := Parent.ClientWidth;
akTop, akBottom: Result := Parent.ClientHeight;
end;
end;
function GetControlMinPos(Control: TControl): Integer;
begin
if Assigned(Control)
then
case ResizeAnchor of
akLeft,akRight: Result := Control.Left;
akTop,akBottom: Result := Control.Top;
end
else
case ResizeAnchor of
akLeft,akTop: Result := 0;
akRight,akBottom: Result := GetParentClientSize;
end;
end;
function GetControlSize(Control: TControl): Integer;
begin
Result := 0;
if Assigned(Control) then
case ResizeAnchor of
akLeft, akRight: Result := Control.Width;
akTop, akBottom: Result := Control.Height;
end;
end;
function GetControlConstraintsMinSize(Control: TControl): Integer;
begin
case ResizeAnchor of
akLeft, akRight: Result := Control.Constraints.EffectiveMinWidth;
akTop, akBottom: Result := Control.Constraints.EffectiveMinHeight;
end;
end;
function GetControlConstraintsMaxSize(Control: TControl): Integer;
begin
case ResizeAnchor of
akLeft, akRight: Result := Control.Constraints.EffectiveMaxWidth;
akTop, akBottom: Result := Control.Constraints.EffectiveMaxHeight;
end;
end;
procedure SetAlignControlSize(NewSize: Integer);
var
NewBounds: TRect;
begin
NewBounds := CurResizeControl.BoundsRect;
//DebugLn('SetAlignControlSize ',DbgSName(Self),' CurResizeControl=',DbgSName(CurResizeControl),' OldBounds=',dbgs(NewBounds),' NewSize=',dbgs(NewSize));
case ResizeAnchor of
akLeft:
NewBounds.Right := NewBounds.Left + NewSize;
akRight:
NewBounds.Left := NewBounds.Right - NewSize;
akTop:
NewBounds.Bottom := NewBounds.Top + NewSize;
akBottom:
NewBounds.Top := NewBounds.Bottom - NewSize;
end;
//DebugLn('SetAlignControlSize ',DbgSName(Self),' CurResizeControl=',DbgSName(CurResizeControl),' NewBounds=',dbgs(NewBounds));
CurResizeControl.BoundsRect := NewBounds;
//DebugLn('SetAlignControlSize ',DbgSName(Self),' CurResizeControl=',DbgSName(CurResizeControl),' NowBounds=',dbgs(CurResizeControl.BoundsRect));
end;
procedure DrawAlignControlSize(NewSize: Integer);
var
NewRect: TRect;
OldSize: Integer;
begin
// get the splitter position
NewRect := BoundsRect;
NewRect.TopLeft := Parent.ClientToScreen(NewRect.TopLeft);
NewRect.BottomRight := Parent.ClientToScreen(NewRect.BottomRight);
// offset it accordinly
OldSize := GetControlSize(CurResizeControl);
case ResizeAnchor of
akLeft:
Types.OffsetRect(NewRect, NewSize - OldSize, 0);
akRight:
Types.OffsetRect(NewRect, OldSize - NewSize, 0);
akTop:
Types.OffsetRect(NewRect, 0, NewSize - OldSize);
akBottom:
Types.OffsetRect(NewRect, 0, OldSize - NewSize);
end;
SetRubberBandRect(FSplitterWindow, NewRect);
end;
function CalcNewSize(StartSize, EndSize, Offset: Integer): Integer;
var
NewSize: Integer;
begin
NewSize := GetControlSize(CurResizeControl);
case ResizeAnchor of
akLeft, akTop: Inc(NewSize, Offset);
akRight, akBottom: Dec(NewSize, Offset);
end;
if NewSize > EndSize then NewSize := EndSize;
if NewSize < StartSize then NewSize := StartSize;
if AutoSnap and (NewSize < FMinSize) then
NewSize := StartSize;
Result := NewSize;
end;
function GetVirtualControlMinPos(Control: TControl): Integer;
begin
case ResizeAnchor of
akLeft: Result := Control.Left;
akRight: Result := Control.BoundsRect.Right;
akTop: Result := Control.Top;
akBottom: Result := Control.BoundsRect.Bottom;
end;
end;
function FindClientControl: TControl;
var
CurControl: TControl;
count: Integer;
begin
Result := nil;
for count := Parent.ControlCount - 1 downto 0 do
begin
CurControl := Parent.Controls[count];
if (CurControl <> Self) and
((CurControl.Align = alClient) or ((Self.Align in [alTop,alBottom]) and
(CurControl.Align in [alLeft,alRight]))) then
begin
Result := CurControl;
Break;
end;
end; // for count
end; // function FindClientControl
function FindLastControl(Control: TControl): TControl;
var
CurControl: TControl;
begin
CurControl := Control;
while Assigned(CurControl) do
begin
Control := CurControl;
CurControl := FindOppositeControl(Control);
end;
Result := Control;
end;
function GetParentsClientLimit: integer;
// returns the maximum size of the CurResizeControl due to parent's client
// area
begin
if ResizeAnchor in [akLeft, akRight] then
begin
if ResizeAnchor = akRight then
Result := CurResizeControl.Left + CurResizeControl.Width - Width
else
Result := Parent.ClientWidth - CurResizeControl.Left - Width;
end else
begin
if ResizeAnchor = akBottom then
Result := CurResizeControl.Top + CurResizeControl.Height - Height
else
Result := Parent.ClientHeight - CurResizeControl.Top - Height;
end;
end;
function GetParentsClientSize: integer;
begin
if ResizeAnchor in [akLeft, akRight] then
Result := Parent.ClientWidth
else
Result := Parent.ClientHeight;
end;
var
StartSize: Integer;
EndSize: Integer;
NewSize: Integer;
i: Integer;
OffsetMaxLower: integer;
OffsetMaxUpper: integer;
CurMaxShrink: integer;
CurMaxEnlarge: integer;
NewRect: TRect;
begin
//DebugLn('TCustomSplitter.MoveSplitter ',DbgSName(Self),' Offset=',dbgs(Offset));
if Offset = 0 then Exit;
if Align in [alLeft, alTop, alRight, alBottom] then
begin
// aligned Splitter
// -> consider aligned siblings for minimum and maximum movement
// get the control to resize
CurResizeControl := GetResizeControl;
if not Assigned(CurResizeControl) then Exit;
CurOtherResizeControl := GetOtherResizeControl;
// calculate minimum size
StartSize := 1;
if not AutoSnap then
Inc(StartSize, Max(FMinSize, GetControlConstraintsMinSize(CurResizeControl)));
if StartSize > 1 then Dec(StartSize);
// calculate maximum size
if Assigned(CurOtherResizeControl) then
EndSize := GetControlSize(CurResizeControl) +
GetControlSize(CurOtherResizeControl) -
Max(FMinSize, GetControlConstraintsMinSize(CurOtherResizeControl))
else
begin
VirtualOppositeControl := FindVirtualOppositeControl(Self);
LastControl := FindLastControl(Self);
case ResizeAnchor of
akLeft, akTop:
begin
if Assigned(VirtualOppositeControl) then
EndSize := GetControlSize(CurResizeControl) +
(GetControlMinPos(VirtualOppositeControl) -
(GetControlMinPos(LastControl) + GetControlSize(LastControl)))
else
EndSize := GetControlSize(CurResizeControl) +
(GetParentClientSize -
GetControlMinPos(LastControl) - GetControlSize(LastControl))
end;
akRight, akBottom:
begin
if Assigned(VirtualOppositeControl) then
EndSize := GetControlSize(CurResizeControl) +
(GetControlMinPos(LastControl) -
(GetControlMinPos(VirtualOppositeControl) + GetControlSize(VirtualOppositeControl)))
else
EndSize := GetControlSize(CurResizeControl) + GetControlMinPos(LastControl);
end;
end;
end;
//DebugLn('TCustomSplitter.MoveSplitter ',DbgSName(Self),' StartSize=',dbgs(StartSize),' EndSize=',dbgs(EndSize),' Offset=',dbgs(Offset));
NewSize := CalcNewSize(StartSize, EndSize, Offset);
// OnCanResize event
if CheckOffset(Offset) and CheckNewSize(NewSize) then
if not FSplitDragging or (ResizeStyle = rsUpdate) then
SetAlignControlSize(NewSize)
else
DrawAlignControlSize(NewSize);
end else
begin
// anchored Splitter
// -> consider anchored siblings for minimum and maximum movement
// OffsetMaxLower = maximum the Splitter can be moved top/left
OffsetMaxLower := Max(0, GetControlMinPos(Self) - FMinSize);
// OffsetMaxUpper = maximum the Splitter can be moved bottom/right
OffsetMaxUpper := Max(0, GetParentsClientSize -GetControlSize(Self) - GetControlMinPos(Self));
//DebugLn(['TCustomSplitter.MoveSplitter OffsetMaxLower=',OffsetMaxLower,' OffsetMaxUpper=',OffsetMaxUpper]);
for i := 0 to AnchoredControlCount - 1 do
begin
CurResizeControl := AnchoredControls[i];
//debugln('TCustomSplitter.MoveSplitter ',DbgSName(Self),' CurResizeControl=',DbgSName(CurResizeControl));
if (CurResizeControl.AnchorSide[ResizeAnchor].Control = Self) or
(CurResizeControl.AnchorSide[OppositeAnchor[ResizeAnchor]].Control = Self) then
begin
// this control uses this Splitter as anchor
// => moving the Splitter will resize CurResizeControl
// => consider the constraints of CurResizeControl
// for minimum and maximum movement
// calculate how much the CurResizeControl can be shrinked
CurMaxShrink := Max(0, GetControlSize(CurResizeControl) - GetControlConstraintsMinSize(CurResizeControl));
// calculate how much the CurResizeControl can be enlarged
CurMaxEnlarge := Max(0, GetControlConstraintsMaxSize(CurResizeControl) - GetControlSize(CurResizeControl));
if (CurMaxEnlarge=0) and (GetControlConstraintsMaxSize(CurResizeControl)=0) then
begin
CurMaxEnlarge := GetParentsClientSize;
if GetControlMinPos(CurResizeControl) < 0 then
dec(CurMaxEnlarge, GetControlMinPos(CurResizeControl));
end;
//debugln('TCustomSplitter.MoveSplitter ',DbgSName(Self),' CurResizeControl=',DbgSName(CurResizeControl),' CurMaxShrink=',dbgs(CurMaxShrink),' CurMaxEnlarge=',dbgs(CurMaxEnlarge),' ConstraintsMax=',dbgs(GetControlConstraintsMaxSize(CurResizeControl)));
// apply to the offset boundaries
if (CurResizeControl.AnchorSide[akLeft].Control = Self) or
(CurResizeControl.AnchorSide[akTop].Control = Self) then
begin
// splitter is left or top of CurResizeControl
if CurMaxEnlarge >= 0 then
OffsetMaxLower := Min(OffsetMaxLower, CurMaxEnlarge);
if CurMaxShrink >= 0 then
OffsetMaxUpper := Min(OffsetMaxUpper, CurMaxShrink);
end else
begin
// splitter is right or bottom of CurResizeControl
if CurMaxShrink >= 0 then
OffsetMaxLower := Min(OffsetMaxLower, CurMaxShrink);
if CurMaxEnlarge >= 0 then
OffsetMaxUpper := Min(OffsetMaxUpper, CurMaxEnlarge);
end;
end;
end;
// apply the offset boundaries to the offset
Offset := Max(Min(Offset, OffsetMaxUpper), -OffsetMaxLower);
//DebugLn(['TCustomSplitter.MoveSplitter Offset=',Offset,' OffsetMaxLower=',OffsetMaxLower,' OffsetMaxUpper=',OffsetMaxUpper]);
// move splitter
if CheckOffset(Offset) then
begin
if not FSplitDragging or (ResizeStyle = rsUpdate) then
begin
if ResizeAnchor in [akLeft, akRight] then
Left := Left + Offset
else
Top := Top + Offset;
end
else
begin
// get the splitter position
NewRect := BoundsRect;
NewRect.TopLeft := Parent.ClientToScreen(NewRect.TopLeft);
NewRect.BottomRight := Parent.ClientToScreen(NewRect.BottomRight);
if ResizeAnchor in [akLeft, akRight] then
Types.OffsetRect(NewRect, Offset, 0)
else
Types.OffsetRect(NewRect, 0, Offset);
SetRubberBandRect(FSplitterWindow, NewRect);
end;
end;
end;
end;
procedure TCustomSplitter.SetSplitterPosition(NewPosition: integer);
begin
//DebugLn('TCustomSplitter.SetSplitterPosition ',DbgSName(Self),' NewPosition=',dbgs(NewPosition),' ',dbgs(GetSplitterPosition));
MoveSplitter(NewPosition - GetSplitterPosition);
end;
function TCustomSplitter.GetSplitterPosition: integer;
begin
if ResizeAnchor in [akLeft, akRight] then
Result := Left
else
Result := Top;
end;
procedure TCustomSplitter.SetBeveled(const AValue: boolean);
begin
if FBeveled = AValue then Exit;
FBeveled := AValue;
Invalidate;
end;
procedure TCustomSplitter.SetMinSize(const AValue: integer);
begin
if (FMinSize=AValue) or (AValue < 1) then Exit;
FMinSize := AValue;
end;
procedure TCustomSplitter.SetResizeAnchor(const AValue: TAnchorKind);
begin
if FResizeAnchor = AValue then Exit;
FResizeAnchor := AValue;
UpdateCursor;
if not (csLoading in ComponentState) then
begin
Align := alNone;
Invalidate;
end;
end;
procedure TCustomSplitter.SetResizeControl(const AValue: TControl);
begin
if Align in [alLeft, alRight, alTop, alBottom] then
begin
if AValue <> nil then
begin
case Align of
alLeft: Left := AValue.Left + 1;
alTop: Top := AValue.Top + 1;
alRight: Left := AValue.Left - 1;
alBottom: Top := AValue.Top - 1;
end;
end;
end
else
AnchorSide[ResizeAnchor].Control := AValue;
end;
procedure TCustomSplitter.StartSplitterMove(const MouseXY: TPoint);
var
NewRect: TRect;
Pattern: HBrush;
begin
if FSplitDragging then Exit;
FSplitDragging := True;
FSplitterStartMouseXY := MouseXY;
FSplitterStartLeftTop := Point(Left, Top);
if ResizeStyle in [rsLine, rsPattern] then
begin
NewRect := BoundsRect;
NewRect.TopLeft := Parent.ClientToScreen(NewRect.TopLeft);
NewRect.BottomRight := Parent.ClientToScreen(NewRect.BottomRight);
if ResizeStyle = rsLine then
Pattern := GetStockObject(BLACK_BRUSH)
else
Pattern := ThemeServices.DottedBrush;
FSplitterWindow := CreateRubberband(NewRect, Pattern);
end;
end;
procedure TCustomSplitter.StopSplitterMove(const MouseXY: TPoint);
var
Offset: Integer;
begin
if FSplitDragging then
begin
case ResizeAnchor of
akLeft, akRight:
Offset := (MouseXY.X - FSplitterStartMouseXY.X) - (Self.Left - FSplitterStartLeftTop.X);
akTop, akBottom:
Offset := (MouseXY.Y - FSplitterStartMouseXY.Y) - (Self.Top - FSplitterStartLeftTop.Y);
else
Offset := 0;
end;
FSplitDragging := False;
if Offset <> 0 then
MoveSplitter(Offset);
if Assigned(OnMoved) then OnMoved(Self);
if ResizeStyle in [rsLine, rsPattern] then
begin
DestroyRubberBand(FSplitterWindow);
FSplitterWindow := 0;
end;
end;
end;
procedure TCustomSplitter.UpdateCursor;
begin
if Enabled then
begin
if ResizeAnchor in [akLeft,akRight] then
Cursor := crHSplit
else
Cursor := crVSplit;
end else
Cursor := crDefault;
end;
procedure TCustomSplitter.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
var
MousePos: TPoint;
begin
inherited MouseDown(Button, Shift, X, Y);
// While resizing X, Y are not valid. Use absolute mouse position.
if Button = mbLeft then
begin
GetCursorPos(MousePos);
StartSplitterMove(MousePos);
end;
end;
procedure TCustomSplitter.MouseMove(Shift: TShiftState; X, Y: Integer);
var
Offset: Integer;
MousePos: TPoint;
begin
inherited MouseMove(Shift, X, Y);
if (ssLeft in Shift) and (Parent <> nil) and (FSplitDragging) then
begin
// While resizing X, Y are not valid. Use the absolute mouse position.
GetCursorPos(MousePos);
case ResizeAnchor of
akLeft, akRight:
Offset := (MousePos.X - FSplitterStartMouseXY.X) - (Self.Left - FSplitterStartLeftTop.X);
akTop, akBottom:
Offset := (MousePos.Y - FSplitterStartMouseXY.Y) - (Self.Top - FSplitterStartLeftTop.Y);
else
Offset := 0;
end;
if Offset <> 0 then
MoveSplitter(Offset);
end;
end;
procedure TCustomSplitter.MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
var
MousePos: TPoint;
begin
inherited MouseUp(Button, Shift, X, Y);
GetCursorPos(MousePos);
StopSplitterMove(MousePos);
end;
function TCustomSplitter.FindAlignControl: TControl;
var
i: Integer;
CurControl: TControl;
BestValue: integer;
procedure FindNearerControl(CurValue, Limit: integer);
begin
if (CurValue <= Limit) and ((Result = nil) or (BestValue < CurValue)) then
begin
BestValue := CurValue;
Result := CurControl;
end;
end;
begin
Result := nil;
BestValue:=0;
if (Parent = nil) then Exit;
if not (Align in [alLeft,alTop,alRight,alBottom]) then exit;
for i := Parent.ControlCount - 1 downto 0 do
begin
CurControl := Parent.Controls[i];
if (CurControl <> Self) and (CurControl.Visible) and
((CurControl.Align = Self.Align) or (CurControl.Align=alClient)) then
begin
case Self.Align of
alLeft: FindNearerControl(CurControl.Left+CurControl.Width,Left);
alTop: FindNearerControl(CurControl.Top+CurControl.Height,Top);
alRight: FindNearerControl(-CurControl.Left,-Left-Width);
alBottom: FindNearerControl(-CurControl.Top,-Top-Height);
end;
end;
end;
end;
function TCustomSplitter.FindAlignOtherControl: TControl;
// if this splitter is aligned, then it returns the control that will be
// resized as well. Normally an alClient aligned control.
// Or: if alTop,alBottom the alLeft/alRight controls.
// And distance to oposite control should be minimal
function CalcDistance(TestControl: TControl): Integer;
var
TestPosition,
MyPosition: Integer;
begin
case Align of
alLeft:
begin
MyPosition := BoundsRect.Right;
TestPosition := TestControl.Left;
Result := TestPosition - MyPosition;
end;
alRight:
begin
MyPosition := Left;
TestPosition := TestControl.BoundsRect.Right;
Result := MyPosition - TestPosition;
end;
alTop:
begin
MyPosition := BoundsRect.Bottom;
TestPosition := TestControl.Top;
Result := TestPosition - MyPosition;
end;
alBottom:
begin
MyPosition := Top;
TestPosition := TestControl.BoundsRect.Bottom;
Result := MyPosition - TestPosition;
end;
else
Result := -1;
end;
end;
var
CurControl: TControl;
i, CurDistance, MinDistance: Integer;
begin
Result := nil;
MinDistance := MaxInt;
for i := Parent.ControlCount-1 downto 0 do
begin
CurControl := Parent.Controls[i];
if (CurControl <> Self) and (CurControl.Visible) and
(
(CurControl.Align = alClient) or
(
(Self.Align in [alTop, alBottom]) and
(CurControl.Align in [alLeft, alRight])
)
) then
begin
CurDistance := CalcDistance(CurControl);
if (CurDistance >= 0) and (CurDistance < MinDistance) then
begin
Result := CurControl;
MinDistance := CurDistance;
if CurDistance = 0 then
Exit;
end;
end;
end;
end;
procedure TCustomSplitter.SetAlign(Value: TAlign);
var
OldWidth: Integer;
OldHeight: Integer;
OldResizeAnchor: TAnchorKind;
begin
OldResizeAnchor:=ResizeAnchor;
case Value of
alLeft: FResizeAnchor:=akLeft;
alTop: FResizeAnchor:=akTop;
alRight: FResizeAnchor:=akRight;
alBottom: FResizeAnchor:=akBottom;
end;
if ((Align = Value) and (OldResizeAnchor = FResizeAnchor)) or (Value = alClient) then
Exit;
OldWidth := Width;
OldHeight := Height;
DisableAlign;
try
inherited SetAlign(Value);
UpdateCursor;
Anchors:=AdaptAnchors(Anchors);
// lfm contains correct size already
if not (csLoading in ComponentState) then
begin
if (OldResizeAnchor in [akLeft,akRight])=(ResizeAnchor in [akLeft,akRight]) then
begin
// keep width and height
SetBounds(Left,Top,OldWidth,OldHeight);
end
else
begin
// resize
if Align in [alLeft,alRight] then
Width:=OldHeight
else if Align in [alTop,alBottom] then
Height:=OldWidth;
end;
end;
finally
EnableAlign;
end;
end;
procedure TCustomSplitter.SetAnchors(const AValue: TAnchors);
var
NewValue: TAnchors;
begin
NewValue:=AdaptAnchors(AValue);
if NewValue = Anchors then exit;
inherited SetAnchors(NewValue);
end;
function TCustomSplitter.AdaptAnchors(const a: TAnchors): TAnchors;
begin
Result:=a;
case Align of
alLeft: Result := Result - [akRight] + [akLeft];
alTop: Result := Result - [akBottom] + [akTop];
alRight: Result := Result + [akRight] - [akLeft];
alBottom: Result := Result + [akBottom] - [akTop];
end;
end;
function TCustomSplitter.CheckNewSize(var NewSize: Integer): Boolean;
begin
Result := True;
if Assigned(OnCanResize) then
OnCanResize(Self, NewSize, Result);
end;
function TCustomSplitter.CheckOffset(var NewOffset: Integer): Boolean;
begin
Result := True;
if Assigned(OnCanOffset) then
OnCanOffset(Self, NewOffset, Result);
end;
procedure TCustomSplitter.CMEnabledChanged(var Message: TLMEssage);
begin
inherited CMEnabledChanged(Message);
UpdateCursor;
end;
procedure TCustomSplitter.Paint;
procedure DrawThemedPattern(ARect: TRect);
const
GripperDetailsPart: array[Boolean] of TThemedRebar =
(
trGripperVert,
trGripper
);
var
GripperRect: TRect;
BgPart: TThemedRebar;
BgDetails, GripperDetails: TThemedElementDetails;
GripperSize: TSize;
begin
GripperDetails := ThemeServices.GetElementDetails(GripperDetailsPart[ResizeAnchor in [akLeft,akRight]]);
if not Enabled then
BgPart := trBandDisabled
else
if FMouseInControl then
BgPart := trBandHot
else
BgPart := trBandNormal;
BgDetails := ThemeServices.GetElementDetails(BgPart);
ThemeServices.DrawElement(Canvas.Handle, BgDetails, ARect, nil);
if Beveled then
ThemeServices.DrawEdge(Canvas.Handle, BgDetails, ARect, BDR_RAISEDOUTER,
BF_ADJUST or BF_RECT, @ARect);
GripperRect := ARect;
GripperSize := ThemeServices.GetDetailSizeForPPI(GripperDetails, Font.PixelsPerInch);
if (GripperSize.cx <> -1) or (GripperSize.cy <> -1) then
begin
if ResizeAnchor in [akLeft,akRight] then
begin
if (GripperRect.Bottom - GripperRect.Top) > GripperSize.cy then
begin
GripperRect.Top := (GripperRect.Top + GripperRect.Bottom - GripperSize.cy) div 2;
GripperRect.Bottom := GripperRect.Top + GripperSize.cy;
end;
end
else
begin
if (GripperRect.Right - GripperRect.Left) > GripperSize.cx then
begin
GripperRect.Left := (GripperRect.Left + GripperRect.Right - GripperSize.cx) div 2;
GripperRect.Right := GripperRect.Left + GripperSize.cx;
end;
end;
end;
ThemeServices.DrawElement(Canvas.Handle, GripperDetails, GripperRect);
end;
begin
inherited Paint;
if not Assigned(OnPaint) then
DrawThemedPattern(ClientRect);
end;
procedure TCustomSplitter.MouseEnter;
begin
inherited MouseEnter;
if csDesigning in ComponentState then exit;
if not FMouseInControl and Enabled and (GetCapture = 0) then
begin
FMouseInControl := True;
invalidate;
end;
end;
procedure TCustomSplitter.MouseLeave;
begin
inherited MouseLeave;
if csDesigning in ComponentState then exit;
if FMouseInControl then
begin
FMouseInControl := False;
invalidate;
end;
end;
constructor TCustomSplitter.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
FResizeStyle := rsUpdate;
FAutoSnap := True;
FBeveled := False;
FMinSize := 30;
FMouseInControl := False;
FResizeAnchor := akLeft;
Align := alLeft;
Width := 5;
// Accessibility
AccessibleRole := larResizeGrip;
AccessibleDescription := rsTSplitterAccessibilityDescription;
end;
procedure TCustomSplitter.AnchorSplitter(Kind: TAnchorKind; AControl: TControl);
procedure AnchorSplitterSides(
ResizeSide,// the side of the Splitter, where AControl is touched and moved
OppositeResizeSide, // opposite of ResizeSide
FixedSide1,// the first non moving side
FixedSide2:// the second non moving side
TAnchorKind);
begin
Anchors:=Anchors-[OppositeResizeSide]+[ResizeSide,FixedSide1,FixedSide2];
AnchorSide[OppositeResizeSide].Control:=nil;
AnchorToNeighbour(ResizeSide,0,AControl);
AnchorParallel(FixedSide1,0,AControl);
AnchorParallel(FixedSide2,0,AControl);
end;
var
OldResizeAnchor: TAnchorKind;
OldWidth: LongInt;
OldHeight: LongInt;
begin
DisableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomSplitter.AnchorSplitter'){$ENDIF};
try
OldWidth := Width;
OldHeight := Height;
OldResizeAnchor := FResizeAnchor;
Align := alNone;
FResizeAnchor := Kind;
UpdateCursor;
case FResizeAnchor of
akLeft: AnchorSplitterSides(akLeft, akRight, akTop, akBottom);
akRight: AnchorSplitterSides(akRight, akLeft, akTop, akBottom);
akTop: AnchorSplitterSides(akTop, akBottom, akLeft, akRight);
akBottom: AnchorSplitterSides(akBottom, akTop, akLeft, akRight);
end;
if (OldResizeAnchor in [akLeft, akRight]) = (ResizeAnchor in [akLeft, akRight]) then
begin
// keep width and height
SetBounds(Left, Top, OldWidth, OldHeight);
end else begin
// resize
if FResizeAnchor in [akLeft, akRight] then
Width := OldHeight
else
Height := OldWidth;
end;
finally
EnableAutoSizing{$IFDEF DebugDisableAutoSizing}('TCustomSplitter.AnchorSplitter'){$ENDIF};
end;
end;
// included by extctrls.pp