mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 01:38:01 +02:00
460 lines
12 KiB
ObjectPascal
460 lines
12 KiB
ObjectPascal
{ $Id$ }
|
|
{
|
|
/***************************************************************************
|
|
pairsplitter.pas
|
|
----------------
|
|
Component Library Controls
|
|
|
|
|
|
***************************************************************************/
|
|
|
|
*****************************************************************************
|
|
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.
|
|
*****************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
TPairSplitter component. A component with two TPairSplitterSide children.
|
|
Both child components can contain other components and the children are
|
|
divided by a splitter which can be dragged by the user.
|
|
}
|
|
unit PairSplitter;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Types, Classes, SysUtils,
|
|
// LazUtils
|
|
LazTracer,
|
|
// LCL
|
|
LCLType, LCLIntf, LMessages, Graphics, Controls, ExtCtrls;
|
|
|
|
type
|
|
TCustomPairSplitter = class;
|
|
|
|
{ TPairSplitterSide }
|
|
|
|
TPairSplitterSide = class(TWinControl)
|
|
private
|
|
function GetSplitter: TCustomPairSplitter;
|
|
protected
|
|
class procedure WSRegisterClass; override;
|
|
procedure SetParent(AParent: TWinControl); override;
|
|
procedure WMPaint(var PaintMessage: TLMPaint); message LM_PAINT;
|
|
procedure Paint; virtual;
|
|
property Align;
|
|
property Anchors;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
public
|
|
property Splitter: TCustomPairSplitter read GetSplitter;
|
|
property Visible;
|
|
property Left;
|
|
property Top;
|
|
property Width;
|
|
property Height;
|
|
published
|
|
property ChildSizing;
|
|
property ClientWidth;
|
|
property ClientHeight;
|
|
property Constraints;
|
|
property Cursor;
|
|
property Enabled;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnResize;
|
|
property ShowHint;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
end;
|
|
|
|
{ TCustomPairSplitter }
|
|
|
|
TPairSplitterType = (
|
|
pstHorizontal,
|
|
pstVertical
|
|
);
|
|
|
|
TCustomPairSplitter = class(TWinControl)
|
|
private
|
|
FPosition: integer;
|
|
FSides: array[0..1] of TPairSplitterSide;
|
|
FSplitterType: TPairSplitterType;
|
|
FDoNotCreateSides: boolean;
|
|
FLoadCursor: TCursor;
|
|
function GetPosition: integer;
|
|
function GetSides(Index: integer): TPairSplitterSide;
|
|
procedure SetPosition(const AValue: integer);
|
|
procedure SetSplitterType(const AValue: TPairSplitterType);
|
|
procedure AddSide(ASide: TPairSplitterSide);
|
|
procedure RemoveSide(ASide: TPairSplitterSide);
|
|
protected
|
|
class procedure WSRegisterClass; override;
|
|
function GetCursor: TCursor; override;
|
|
procedure SetCursor(Value: TCursor); override;
|
|
class function GetControlClassDefaultSize: TSize; override;
|
|
public
|
|
constructor Create(TheOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
procedure CreateWnd; override;
|
|
procedure UpdatePosition;
|
|
procedure CreateSides;
|
|
procedure Loaded; override;
|
|
function ChildClassAllowed(ChildClass: TClass): boolean; override;
|
|
public
|
|
property Cursor default crHSplit;
|
|
property Sides[Index: integer]: TPairSplitterSide read GetSides;
|
|
property SplitterType: TPairSplitterType read FSplitterType
|
|
write SetSplitterType default pstHorizontal;
|
|
property Position: integer read GetPosition write SetPosition;
|
|
end;
|
|
|
|
|
|
{ TPairSplitter }
|
|
|
|
TPairSplitter = class(TCustomPairSplitter)
|
|
published
|
|
property Align;
|
|
property Anchors;
|
|
property BorderSpacing;
|
|
property Constraints;
|
|
property Color;
|
|
property Cursor;
|
|
property Enabled;
|
|
property OnMouseDown;
|
|
property OnMouseEnter;
|
|
property OnMouseLeave;
|
|
property OnMouseMove;
|
|
property OnMouseUp;
|
|
property OnMouseWheel;
|
|
property OnMouseWheelDown;
|
|
property OnMouseWheelUp;
|
|
property OnResize;
|
|
property OnChangeBounds;
|
|
property ParentShowHint;
|
|
property PopupMenu;
|
|
property Position;
|
|
property ShowHint;
|
|
property SplitterType;
|
|
property Visible;
|
|
end;
|
|
|
|
procedure Register;
|
|
|
|
implementation
|
|
|
|
uses
|
|
WSPairSplitter;
|
|
|
|
procedure Register;
|
|
begin
|
|
RegisterComponents('Additional',[TPairSplitter]);
|
|
RegisterNoIcon([TPairSplitterSide]);
|
|
end;
|
|
|
|
{ TPairSplitterSide }
|
|
|
|
function TPairSplitterSide.GetSplitter: TCustomPairSplitter;
|
|
begin
|
|
if Parent is TCustomPairSplitter then
|
|
Result:=TCustomPairSplitter(Parent)
|
|
else
|
|
Result:=nil;
|
|
end;
|
|
|
|
class procedure TPairSplitterSide.WSRegisterClass;
|
|
begin
|
|
inherited WSRegisterClass;
|
|
RegisterPairSplitterSide;
|
|
end;
|
|
|
|
procedure TPairSplitterSide.SetParent(AParent: TWinControl);
|
|
var
|
|
ASplitter: TCustomPairSplitter;
|
|
DeletingSplitter: Boolean;
|
|
begin
|
|
CheckNewParent(AParent);
|
|
// remove from side list of old parent
|
|
ASplitter := Splitter;
|
|
if ASplitter <> nil then begin
|
|
ASplitter.RemoveSide(Self);
|
|
DeletingSplitter := (csDestroying in ASplitter.ComponentState)
|
|
or (wcfDesignerDeleting in FWinControlFlags);
|
|
end
|
|
else
|
|
DeletingSplitter := False;
|
|
|
|
inherited SetParent(AParent);
|
|
|
|
if not DeletingSplitter then begin
|
|
// add to side list of new parent
|
|
ASplitter:=Splitter;
|
|
if ASplitter <> nil then
|
|
ASplitter.AddSide(Self);
|
|
end;
|
|
end;
|
|
|
|
procedure TPairSplitterSide.WMPaint(var PaintMessage: TLMPaint);
|
|
begin
|
|
if (csDestroying in ComponentState) or (not HandleAllocated) then
|
|
Exit;
|
|
Include(FControlState, csCustomPaint);
|
|
inherited WMPaint(PaintMessage);
|
|
Paint;
|
|
Exclude(FControlState, csCustomPaint);
|
|
end;
|
|
|
|
procedure TPairSplitterSide.Paint;
|
|
var
|
|
ACanvas: TControlCanvas;
|
|
begin
|
|
if csDesigning in ComponentState then
|
|
begin
|
|
ACanvas := TControlCanvas.Create;
|
|
with ACanvas do
|
|
begin
|
|
Control := Self;
|
|
Pen.Style := psDash;
|
|
Frame(0,0,Width-1,Height-1);
|
|
Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TPairSplitterSide.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FCompStyle := csPairSplitterSide;
|
|
ControlStyle := ControlStyle + [csAcceptsControls];
|
|
// A flag custom made for TPairSplitterSide.
|
|
Include(FWinControlFlags, wcfSpecialSubControl);
|
|
end;
|
|
|
|
destructor TPairSplitterSide.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
{ TCustomPairSplitter }
|
|
|
|
function TCustomPairSplitter.GetSides(Index: integer): TPairSplitterSide;
|
|
begin
|
|
if (Index < 0) or (Index > 1) then
|
|
RaiseGDBException('TCustomPairSplitter.GetSides: Index out of bounds');
|
|
Result := FSides[Index];
|
|
end;
|
|
|
|
function TCustomPairSplitter.GetPosition: integer;
|
|
begin
|
|
if HandleAllocated and (not (csLoading in ComponentState)) then
|
|
UpdatePosition;
|
|
Result := FPosition;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.SetPosition(const AValue: integer);
|
|
begin
|
|
if (FPosition = AValue) and
|
|
(TWSCustomPairSplitterClass(WidgetSetClass).GetPosition(Self) = FPosition)
|
|
then
|
|
Exit;
|
|
|
|
FPosition := AValue;
|
|
if FPosition < 0 then
|
|
FPosition := 0;
|
|
if HandleAllocated and (not (csLoading in ComponentState)) then
|
|
TWSCustomPairSplitterClass(WidgetSetClass).SetPosition(Self, FPosition);
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.SetSplitterType(const AValue: TPairSplitterType);
|
|
const
|
|
DefaultCursors: array[TPairSplitterType] of TCursor =
|
|
(
|
|
{ pstHorizontal } crHSplit,
|
|
{ pstVertical } crVSplit
|
|
);
|
|
begin
|
|
if FSplitterType = AValue then
|
|
Exit;
|
|
|
|
if Cursor = DefaultCursors[FSplitterType] then
|
|
Cursor := DefaultCursors[AValue];
|
|
|
|
FSplitterType := AValue;
|
|
|
|
// TODO: Remove RecreateWnd
|
|
if HandleAllocated then
|
|
RecreateWnd(Self);
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.AddSide(ASide: TPairSplitterSide);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if ASide = nil then
|
|
Exit;
|
|
i := Low(FSides);
|
|
repeat
|
|
if FSides[i] = ASide then
|
|
Exit;
|
|
if FSides[i] =nil then
|
|
begin
|
|
FSides[i] := ASide;
|
|
if HandleAllocated then
|
|
TWSCustomPairSplitterClass(WidgetSetClass).AddSide(Self, ASide, i);
|
|
break;
|
|
end;
|
|
inc(i);
|
|
if i > High(FSides) then
|
|
RaiseGDBException('TCustomPairSplitter.AddSide no free side left');
|
|
until False;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.RemoveSide(ASide: TPairSplitterSide);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if ASide = nil then
|
|
Exit;
|
|
for i := Low(FSides) to High(FSides) do
|
|
if FSides[i]=ASide then
|
|
begin
|
|
if HandleAllocated and ASide.HandleAllocated then
|
|
TWSCustomPairSplitterClass(WidgetSetClass).RemoveSide(Self, ASide, i);
|
|
FSides[i] := nil;
|
|
end;
|
|
// if the user deletes a side at designtime, autocreate a new one
|
|
if (ComponentState * [csDesigning,csDestroying] = [csDesigning])
|
|
and not (wcfDesignerDeleting in FWinControlFlags) then
|
|
CreateSides;
|
|
end;
|
|
|
|
class procedure TCustomPairSplitter.WSRegisterClass;
|
|
begin
|
|
inherited WSRegisterClass;
|
|
RegisterCustomPairSplitter;
|
|
end;
|
|
|
|
function TCustomPairSplitter.GetCursor: TCursor;
|
|
begin
|
|
// Paul Ishenin: I do not know another method to tell internal splitter about
|
|
// cursor changes
|
|
|
|
// if widgetset class do not want to get cursor (has no internal splitter) then
|
|
// use default lcl handler
|
|
if not TWSCustomPairSplitterClass(WidgetSetClass).GetSplitterCursor(Self, Result) then
|
|
Result := inherited GetCursor;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.SetCursor(Value: TCursor);
|
|
begin
|
|
FLoadCursor := Value;
|
|
if not HandleAllocated then
|
|
Exit;
|
|
// if widgetset class do not want to set cursor (has no internal splitter) then
|
|
// use default lcl handler
|
|
if not TWSCustomPairSplitterClass(WidgetSetClass).SetSplitterCursor(Self, Value) then
|
|
inherited SetCursor(Value);
|
|
end;
|
|
|
|
class function TCustomPairSplitter.GetControlClassDefaultSize: TSize;
|
|
begin
|
|
Result.CX := 90;
|
|
Result.CY := 90;
|
|
end;
|
|
|
|
constructor TCustomPairSplitter.Create(TheOwner: TComponent);
|
|
begin
|
|
inherited Create(TheOwner);
|
|
FCompStyle := csPairSplitter;
|
|
ControlStyle := ControlStyle - [csAcceptsControls];
|
|
FSplitterType := pstHorizontal;
|
|
Cursor := crHSplit;
|
|
with GetControlClassDefaultSize do
|
|
SetInitialBounds(0, 0, CX, CY);
|
|
FPosition:=45;
|
|
if not (csDesigning in ComponentState) then
|
|
CreateSides;
|
|
end;
|
|
|
|
destructor TCustomPairSplitter.Destroy;
|
|
begin
|
|
fDoNotCreateSides:=true;
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.CreateWnd;
|
|
var
|
|
i: Integer;
|
|
APosition: Integer;
|
|
begin
|
|
CreateSides;
|
|
inherited CreateWnd;
|
|
for i := Low(FSides) to High(FSides) do
|
|
if FSides[i] <> nil then
|
|
TWSCustomPairSplitterClass(WidgetSetClass).AddSide(Self, FSides[i], i);
|
|
APosition := FPosition;
|
|
TWSCustomPairSplitterClass(WidgetSetClass).SetPosition(Self, APosition);
|
|
SetCursor(FLoadCursor);
|
|
if not (csLoading in ComponentState) then
|
|
FPosition := APosition;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.UpdatePosition;
|
|
var
|
|
CurPosition: Integer;
|
|
begin
|
|
if HandleAllocated then
|
|
begin
|
|
CurPosition := -1;
|
|
TWSCustomPairSplitterClass(WidgetSetClass).SetPosition(Self, CurPosition);
|
|
FPosition := CurPosition;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.CreateSides;
|
|
var
|
|
ASide: TPairSplitterSide;
|
|
i: Integer;
|
|
begin
|
|
if fDoNotCreateSides or (ComponentState * [csLoading,csDestroying] <> [])
|
|
or ((Owner<>nil) and (csLoading in Owner.ComponentState)) then exit;
|
|
// create the missing side controls
|
|
for i := Low(FSides) to High(FSides) do
|
|
if FSides[i]=nil then
|
|
begin
|
|
// For streaming it is important that the side controls are owned by
|
|
// the owner of the splitter
|
|
ASide:=TPairSplitterSide.Create(Owner);
|
|
ASide.Parent:=Self;
|
|
end;
|
|
end;
|
|
|
|
procedure TCustomPairSplitter.Loaded;
|
|
begin
|
|
inherited Loaded;
|
|
CreateSides;
|
|
if HandleAllocated then
|
|
TWSCustomPairSplitterClass(WidgetSetClass).SetPosition(Self, FPosition);
|
|
end;
|
|
|
|
function TCustomPairSplitter.ChildClassAllowed(ChildClass: TClass): boolean;
|
|
begin
|
|
Result := ChildClass.InheritsFrom(TPairSplitterSide) or
|
|
ChildClass.InheritsFrom(TSplitter);
|
|
end;
|
|
|
|
end.
|