lazarus/components/sparta/mdi/source/sparta_basicresizeframe.pas

1148 lines
31 KiB
ObjectPascal

unit sparta_BasicResizeFrame;
{$mode delphi}{$H+}
interface
uses
Classes, Types, contnrs, SysUtils, Math,
FileUtil,
LCLType, LCLIntf, LMessages, Forms, Controls, ExtCtrls, StdCtrls, Graphics, Menus,
sparta_InterfacesMDI;
type
TPositioningCode = (pcPositioning, pcPositioningEnd);
TPositioningKind = set of (pkBottom, pkRight);
TPositioningEvent = procedure(Sender: TObject; PositioningKind: TPositioningKind; PositioningCode: TPositioningCode) of object;
{ TBasicResizeFrame }
TResizerFrameClass = class of TBasicResizeFrame;
TBasicResizeFrame = class(TFrame, IResizeFrame)
iResizerLineImg: TImage;
pFormHandler: TPanel;
pFakeMenu: TPanel;
pBG: TPanel;
pB: TPanel;
pClient: TPanel;
pL: TPanel;
pMarginB: TPanel;
pMarginL: TPanel;
pMarginR: TPanel;
pMarginT: TPanel;
pR: TPanel;
pT: TPanel;
procedure pBGPaint(Sender: TObject);
procedure pFakeMenuPaint(Sender: TObject);
procedure sbVerticalScroll(Sender: TObject; ScrollCode: TScrollCode;
var ScrollPos: Integer);
procedure sbHorizontalScroll(Sender: TObject; ScrollCode: TScrollCode;
var ScrollPos: Integer);
public const
SIZER_RECT_SIZE = 8;
SIZER_LINE_WIDTH = 8;
private
FVerticalScrollPos: Integer;
FHorizontalScrollPos: Integer;
FDesignedForm: IDesignedForm;
FBackground: IDesignedFormBackground;
FFakeFocusControl: TWinControl;
procedure FakeExitEnter(Sender: TObject);
procedure FakeKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
procedure FakeKeyUp(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
procedure FakeUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
private
FOnNodePositioning: TPositioningEvent;
FOnHorizontalScroll, FOnVerticalScroll: TScrollEvent;
FLastRightMarign: Integer;
FLastBottomMarign: Integer;
FNodePositioning: Boolean;
FOldPos, FDelta: TPoint;
FPositioningKind: TPositioningKind;
FMaxWidth, FMaxHeight: Integer;
FLastClientWidth, FLastClientHeight: Integer;
FLastDesignedWidthToScroll, FLastDesignedHeightToScroll: Integer;
FOldHasMainMenu: Boolean;
FDesignerModified: Boolean;
FSizerLineWidth: Integer;
FSizerRectSize: Integer;
function HasMainMenu: Boolean;
procedure AppOnIdle(Sender: TObject; var {%H-}Done: Boolean);
procedure PanelPaint(Sender: TObject);
procedure BGChangeBounds(Sender: TObject);
procedure CreateNodes;
procedure NodeMouseDown(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
procedure NodeMouseMove(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
procedure NodeMouseUp(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
function GetRightMargin: Integer;
function GetBottomMargin: Integer;
// dependent on scroll position
// for Vertical
function BottomSizerRectHeight: Integer;
function BottomSizerLineWidth: Integer;
function TopSizerRectTop: Integer;
function TopSizerLineWidth: Integer;
function VerticalSizerLineLength: Integer;
// for Horizontal
function RightSizerRectWidth: Integer;
function RightSizerLineWidth: Integer;
function LeftSizerRectLeft: Integer;
function LeftSizerLineWidth: Integer;
function HorizontalSizerLineLength: Integer;
procedure AdjustFormHandler;
function GetMenuHeight: Integer;
protected
FNodes: TObjectList;
protected
procedure TryBoundDesignedForm; virtual;
procedure BeginFormSizeUpdate(Sender: TObject); virtual;
procedure EndFormSizeUpdate(Sender: TObject); virtual;
protected { IResizeFrame }
procedure HideSizeRects;
procedure ShowSizeRects;
procedure PositionNodes; overload;
function DesignedWidthToScroll: Integer;
function DesignedHeightToScroll: Integer;
procedure ClientChangeBounds; overload;
function GetFrame: TCustomFrame;
function GetVerticalScrollPos: Integer;
procedure SetVerticalScrollPos(AValue: Integer);
function GetHorizontalScrollPos: Integer;
procedure SetHorizontalScrollPos(AValue: Integer);
function GetBackgroundPanel: TPanel;
function GetBackgroundMargin(const AIndex: Integer): Integer;
function GetNewSize: TPoint;
function GetFormHandler: TPanel;
function GetNodePositioning: Boolean;
function GetDesignedForm: IDesignedForm;
procedure SetDesignedForm(const AValue: IDesignedForm);
function GetSizerRectSize: Integer;
function GetSizerLineWidth: Integer;
public { IResizeFrame }
procedure DesignerSetFocus;
public
constructor Create(TheOwner: TComponent); override;
destructor Destroy; override;
property DesignedForm: IDesignedForm read GetDesignedForm write SetDesignedForm;
procedure PositionNodes(AroundControl: TWinControl); overload;
property NodePositioning: Boolean read GetNodePositioning;
procedure ClientChangeBounds(Sender: TObject); overload;
property RightMargin: Integer read GetRightMargin;
property BottomMargin: Integer read GetBottomMargin;
property OnNodePositioning: TPositioningEvent read FOnNodePositioning write FOnNodePositioning;
property BgLeftMargin: Integer index 0 read GetBackgroundMargin;
property BgTopMargin: Integer index 1 read GetBackgroundMargin;
property BgRightMargin: Integer index 2 read GetBackgroundMargin;
property BgBottomMargin: Integer index 3 read GetBackgroundMargin;
property SizerRectSize: Integer read FSizerRectSize;
property SizerLineWidth: Integer read FSizerLineWidth;
procedure HideSizeControls;
procedure ShowSizeControls;
procedure OnModified;
property VerticalScrollPos: Integer read GetVerticalScrollPos write SetVerticalScrollPos;
property HorizontalScrollPos: Integer read GetHorizontalScrollPos write SetHorizontalScrollPos;
end;
implementation
{$R *.lfm}
{ Node grip indices are as follows:
1
0 +----+----+ 2
| |
7 + + 3
| |
6 +----+----+ 4
5
Only grips 3, 4, and 5 are sizeable }
procedure TBasicResizeFrame.pFakeMenuPaint(Sender: TObject);
var
MenuRect: Types.TRect;
Menu: TMainMenu;
X, Y, I: Integer;
LCanvas: TCanvas;
begin
//fake paint menu
MenuRect := pFakeMenu.ClientRect;
LCanvas := pFakeMenu.Canvas;
LCanvas.Brush.Color := clMenuBar;
LCanvas.FillRect(MenuRect);
// pFakeMenu is visible only when HasMainMenu is true
// but FDesignedForm can be nil if the designer is painted before it has been assigned
if not HasMainMenu then
Exit;
Menu := FDesignedForm.Form.Menu;
LCanvas.Font.Color := clMenuText;
X := 5;
Y := (MenuRect.Top+MenuRect.Bottom-LCanvas.TextHeight('Hg')) div 2;
for I := 0 to Menu.Items.Count-1 do
if Menu.Items[I].Visible then
begin
LCanvas.TextOut(X, Y, Menu.Items[I].Caption);
Inc(X, LCanvas.TextWidth(Menu.Items[I].Caption) + 10);
end;
end;
procedure TBasicResizeFrame.pBGPaint(Sender: TObject);
begin
pBG.SendToBack;
end;
procedure TBasicResizeFrame.sbVerticalScroll(Sender: TObject;
ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
if ScrollCode <> scEndScroll then
HideSizeRects
else
ShowSizeRects;
FVerticalScrollPos := ScrollPos;
PositionNodes(Self);
if Assigned(FOnVerticalScroll)
// for refresh from this class, pass sender as nil.
// In other case program will go into infinity loop
and (Sender <> nil) then
FOnVerticalScroll(Sender, ScrollCode, ScrollPos);
end;
procedure TBasicResizeFrame.sbHorizontalScroll(Sender: TObject;
ScrollCode: TScrollCode; var ScrollPos: Integer);
begin
if ScrollCode <> scEndScroll then
HideSizeRects
else
ShowSizeRects;
FHorizontalScrollPos := ScrollPos;
PositionNodes(Self);
if Assigned(FOnHorizontalScroll)
// for refresh from this class, pass sender as nil.
// In other case program will go into infinity loop
and (Sender <> nil) then
FOnHorizontalScroll(Sender, ScrollCode, ScrollPos);
end;
{ TResizerFrame }
// Tiles the source image over the given target canvas
procedure TileImage(const ASource: TImage; ATarget: TCanvas; AX, AY,
AWidth, AHeight: Integer);
var
LX, LY, LDeltaX, LDeltaY: Integer;
begin
LDeltaX := ASource.Width;
LDeltaY := ASource.Height;
LY := 0;
while LY < AHeight do
begin
LX := 0;
while LX < AWidth do
begin
ATarget.Draw(AX + LX, AY + LY, ASource.Picture.graphic);
Inc(LX, LDeltaX);
end;
Inc(LY, LDeltaY);
end;
end;
procedure TBasicResizeFrame.PanelPaint(Sender: TObject);
var
LWidth, LHeight: Integer;
LOldColor: TColor;
LCanvas: TCanvas;
begin
if FNodePositioning then
Exit;
if (Sender = pR) or (Sender = pL) then
begin
LWidth := SizerLineWidth;
LHeight := Height;
end else
begin
LWidth := Width;
LHeight := SizerLineWidth;
end;
LCanvas := (Sender as TPanel).Canvas;
if FFakeFocusControl.Focused then
begin
LOldColor := LCanvas.Brush.Color;
LCanvas.Brush.Color := $FFEEDD;
LCanvas.FillRect(0, 0, LWidth, LHeight);
LCanvas.Brush.Color := LOldColor;
end;
TileImage(iResizerLineImg, LCanvas, 0, 0, LWidth, LHeight);
end;
procedure TBasicResizeFrame.ClientChangeBounds(Sender: TObject);
begin
if (DesignedForm = nil) or FNodePositioning then
Exit;
FLastClientWidth := pClient.Width;
FLastClientHeight := pClient.Height;
(*
DesignedForm.BeginUpdate;
DesignedForm.RealLeft := 0;
DesignedForm.RealTop := 0;
DesignedForm.RealWidth := pClient.Width;
DesignedForm.RealHeight := pClient.Height;
DesignedForm.EndUpdate;
*)
end;
procedure TBasicResizeFrame.BGChangeBounds(Sender: TObject);
begin
PositionNodes(Self);
end;
procedure TBasicResizeFrame.HideSizeRects;
var
p: TObject;
wc: TWinControl absolute p;
begin
for p in FNodes do
if not (wc is TPanel) then
wc.Visible := False;
end;
procedure TBasicResizeFrame.HideSizeControls;
begin
pL.Repaint;
pT.Repaint;
pR.Repaint;
pB.Repaint;
HideSizeRects;
pBG.SendToBack;
end;
procedure TBasicResizeFrame.ShowSizeRects;
var
p: TObject;
wc: TWinControl absolute p;
begin
for p in FNodes do
wc.Visible := True;
end;
procedure TBasicResizeFrame.PositionNodes;
begin
PositionNodes(Self);
end;
procedure TBasicResizeFrame.ShowSizeControls;
begin
pL.Repaint;
pT.Repaint;
pR.Repaint;
pB.Repaint;
ShowSizeRects;
//pBG.Visible := True;
end;
procedure TBasicResizeFrame.CreateNodes;
var
Node: Integer;
Panel: TPanel;
begin
for Node := 0 to 7 do
begin
Panel := TPanel.Create(self);
with Panel do
begin
BevelOuter := bvNone;
Color := clBlack;
Name := 'Node' + IntToStr(Node);
Caption:='';
Width := SIZER_RECT_SIZE; // scaled dynamically by LCL
Height := SIZER_RECT_SIZE; // scaled dynamically by LCL
Parent := Self;
Visible := True;
FNodes.Add(Panel);
case Node of
// on mac there is no cursor for crNWSE ( https://bugs.freepascal.org/view.php?id=32194#c101876 )
{0,}4: Cursor := {$IFDEF MACOS}crSizeAll{$ELSE}crSizeNWSE{$ENDIF};
{1,}5: Cursor := crSizeNS;
//{2,}6: Cursor := $IFDEF MACOS}crSizeAll{$ELSE}crSizeNESW{$ENDIF};
3{,7}: Cursor := crSizeWE;
end;
if Node in [3,4,5] then
begin
OnMouseDown := NodeMouseDown;
OnMouseMove := NodeMouseMove;
OnMouseUp := NodeMouseUp;
end;
with TShape.Create(Panel) do
begin
Parent := Panel;
Align:= alClient;
Enabled := False;
if Node in [3,4,5] then
Brush.Color:=clBtnFace
else
Brush.Color:=clGray;
end;
end;
end;
// extra resizers
pB.OnMouseDown := NodeMouseDown;
pB.OnMouseMove := NodeMouseMove;
pB.OnMouseUp := NodeMouseUp;
pR.OnMouseDown := NodeMouseDown;
pR.OnMouseMove := NodeMouseMove;
pR.OnMouseUp := NodeMouseUp;
FNodes.Add(pL);
FNodes.Add(pT);
FNodes.Add(pR);
FNodes.Add(pB);
end;
procedure TBasicResizeFrame.NodeMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var
LCtrlPoint: TPoint;
begin
{ TShape in TBasicResizeFrame.CreateNodes is disabled, anyway in future can be usefull
if Sender is TGraphicControl then
Sender := TGraphicControl(Sender).Parent;}
if (Enabled) and (Sender is TWinControl) then
begin
FNodePositioning:=True;
BeginFormSizeUpdate(Sender);
// when we start resizing the rules do not apply to us :)
FMaxWidth := Constraints.MaxWidth;
FMaxHeight := Constraints.MaxHeight;
Constraints.MaxWidth := 0;
Constraints.MaxHeight := 0;
with pClient do
begin
Align := alClient;
if pBG.Left + BgLeftMargin <= 0 then
BorderSpacing.Left := Max(-pBG.Left - (FHorizontalScrollPos - SizerRectSize), 0)
else
BorderSpacing.Left := Max(pBG.Left + BgLeftMargin, 0);
if pBG.Top + BgTopMargin <= 0 then
BorderSpacing.Top := Max(-pBG.Top - (FVerticalScrollPos - SizerRectSize), 0)
else
BorderSpacing.Top := Max(pBG.Top + BgTopMargin, 0);
BorderSpacing.Right := Max(Self.Width - (pR.Left - BgRightMargin), 0);
BorderSpacing.Bottom := Max(Self.Height - (pB.Top - BgBottomMargin), 0);
end;
{$IF Defined(LCLWin32) or Defined(LCLWin64)}
SetCapture(TWinControl(Sender).Handle);
{$ENDIF}
GetCursorPos(FOldPos);
// perform first "click delta" to reduce leap
// + calculate delta created by scrollbars and theirs position...
FillChar(FDelta, SizeOf(FDelta), #0);
LCtrlPoint := (Sender as TWinControl).ScreenToClient(Mouse.CursorPos);
if Sender = pR then
begin
FDelta.X := -(LCtrlPoint.x - RightSizerLineWidth) + RightMargin + Left;
FPositioningKind := [pkRight];
end
else if Sender = pB then
begin
FDelta.Y := -(LCtrlPoint.y - BottomSizerLineWidth) + BottomMargin + Top;
FPositioningKind := [pkBottom];
end
else
case FNodes.IndexOf(Sender) of
3: // middle right
begin
FDelta.X := -(LCtrlPoint.x - RightSizerRectWidth) + RightMargin + Left;
FPositioningKind := [pkRight];
end;
4: // right bottom
begin
FDelta.X := -(LCtrlPoint.x - RightSizerRectWidth) + RightMargin + Left;
FDelta.Y := -(LCtrlPoint.y - BottomSizerRectHeight) + BottomMargin + Top;
FPositioningKind := [pkRight, pkBottom];
end;
5: // middle bottom
begin
FDelta.Y := -(LCtrlPoint.y - BottomSizerRectHeight) + BottomMargin + Top;
FPositioningKind := [pkBottom];
end;
end;
end;
end;
procedure TBasicResizeFrame.NodeMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
var
newPos: TPoint;
frmPoint : TPoint;
OldRect: TRect;
AdjL,AdjR,AdjT,AdjB: Boolean;
begin
{ TShape in TBasicResizeFrame.CreateNodes is disabled, anyway in future can be usefull
// handle TPanel for resizing rectangles
if Sender is TGraphicControl then
Sender := TGraphicControl(Sender).Parent;}
if FNodePositioning then
begin
with TWinControl(Sender) do
begin
newPos := Point(0, 0);
GetCursorPos(newPos);
if (newPos.x = FOldPos.x) and (newPos.y = FOldPos.y) then
Exit;
HideSizeControls;
UpdateWindow(pBG.Handle);
UpdateWindow(Self.Handle);
UpdateWindow(Self.Parent.Handle);
with Self do
begin //resize
frmPoint := Self.ScreenToClient(Mouse.CursorPos);
frmPoint.x:= frmPoint.x + FDelta.x;
frmPoint.y:= frmPoint.y + FDelta.y;
OldRect := Self.BoundsRect;
AdjL := False;
AdjR := False;
AdjT := False;
AdjB := False;
case FNodes.IndexOf(TWinControl(Sender)) of
0: begin
//AdjL := True;
//AdjT := True;
end;
1: begin
//AdjT := True;
end;
2: begin
//AdjR := True;
//AdjT := True;
end;
3, 10: begin
AdjR := True;
end;
4: begin
AdjR := True;
AdjB := True;
end;
5, 11: begin
AdjB := True;
end;
6: begin
//AdjL := True;
//AdjB := True;
end;
7: begin
//AdjL := True;
end;
end;
if AdjL then
OldRect.Left := frmPoint.X;
if AdjR then
OldRect.Right := frmPoint.X;
if AdjT then
OldRect.Top := frmPoint.Y;
if AdjB then
OldRect.Bottom := frmPoint.Y;
SetBounds(OldRect.Left,OldRect.Top,OldRect.Right - OldRect.Left,OldRect.Bottom - OldRect.Top);
end;
//move node
Left := Left - FOldPos.X + newPos.X;
Top := Top - FOldPos.Y + newPos.Y;
FOldPos := newPos;
end;
PositionNodes(Self);
if Assigned(OnNodePositioning) then
OnNodePositioning(Self, FPositioningKind, pcPositioning);
// the same operation as belowe exist in ClientChangeBounds but it is
// disabled for FNodePositioning = true
// we need to refresh this values after OnNodePositioning
FLastClientWidth := pClient.Width;
FLastClientHeight:= pClient.Height;
end;
end;
procedure TBasicResizeFrame.NodeMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
{ TShape in TBasicResizeFrame.CreateNodes is disabled, anyway in future can be usefull
if Sender is TGraphicControl then
Sender := TGraphicControl(Sender).Parent;}
if FNodePositioning then
begin
Screen.Cursor := crDefault;
{$IF Defined(LCLWin32) or Defined(LCLWin64)}
ReleaseCapture;
{$ENDIF}
Constraints.MaxWidth := FMaxWidth;
Constraints.MaxHeight := FMaxHeight;
FNodePositioning := False;
ShowSizeControls;
if Assigned(OnNodePositioning) then
OnNodePositioning(Sender, FPositioningKind, pcPositioningEnd);
FPositioningKind := [];
FNodePositioning := False;
pClient.Align := alNone;
BorderSpacing.Left := 0;
BorderSpacing.Top := 0;
BorderSpacing.Right := 0;
BorderSpacing.Bottom := 0;
PositionNodes(Self);
EndFormSizeUpdate(Sender);
// after resizing, TFrame is frozen in Windows OS
// this is trick to workaraund IDE bug. Also for proper size for normal form
TryBoundDesignedForm;
// for small resizes, designed form is moved on the top and on the bottom
// is showed white block - to stop this we need to move pClient to right position
PositionNodes;
ShowSizeControls;
end;
end;
procedure TBasicResizeFrame.OnModified;
begin
FDesignerModified := True;
end;
function TBasicResizeFrame.GetRightMargin: Integer;
begin
if not FNodePositioning then
FLastRightMarign := Width - (pR.Left + pR.Width);
Result := FLastRightMarign;
end;
function TBasicResizeFrame.HasMainMenu: Boolean;
var
I: Integer;
begin
Result := False;
if (FDesignedForm<>nil) and (FDesignedForm.Form.Menu<>nil)
and not (csDestroying in FDesignedForm.Form.Menu.ComponentState)
and (FDesignedForm.Form.Menu.Items.Count>0)
then
for I := 0 to FDesignedForm.Form.Menu.Items.Count-1 do
if FDesignedForm.Form.Menu.Items[I].Visible then
Exit(True);
end;
function TBasicResizeFrame.GetBottomMargin: Integer;
begin
if not FNodePositioning then
FLastBottomMarign := Height - (pB.Top + pB.Height);
Result := FLastBottomMarign;
end;
{-----------------------------------------------------------------------------------------------------------------------
for Vertical scroll
{----------------------------------------------------------------------------------------------------------------------}
function TBasicResizeFrame.BottomSizerRectHeight: Integer;
begin
Result := SizerRectSize;
end;
function TBasicResizeFrame.BottomSizerLineWidth: Integer;
begin
Result := SizerLineWidth;
end;
function TBasicResizeFrame.TopSizerRectTop: Integer;
begin
Result := -FVerticalScrollPos;
end;
function TBasicResizeFrame.TopSizerLineWidth: Integer;
begin
Result := SizerLineWidth;
end;
function TBasicResizeFrame.VerticalSizerLineLength: Integer;
begin
Result := Height - BottomMargin;
end;
{-----------------------------------------------------------------------------------------------------------------------
for Horizontal scroll
{----------------------------------------------------------------------------------------------------------------------}
function TBasicResizeFrame.RightSizerRectWidth: Integer;
begin
Result := SizerRectSize;
end;
function TBasicResizeFrame.RightSizerLineWidth: Integer;
begin
Result := SizerLineWidth;
end;
function TBasicResizeFrame.LeftSizerRectLeft: Integer;
begin
Result := -FHorizontalScrollPos;
end;
function TBasicResizeFrame.LeftSizerLineWidth: Integer;
begin
Result := SizerLineWidth;
end;
function TBasicResizeFrame.HorizontalSizerLineLength: Integer;
begin
Result := Width - RightMargin;
end;
procedure TBasicResizeFrame.AdjustFormHandler;
begin
pFormHandler.Left:=(-FDesignedForm.Form.Left)-(FDesignedForm.PositionDelta.x+ifthen(FHorizontalScrollPos-SizerLineWidth>0,FHorizontalScrollPos-SizerLineWidth,0));
pFormHandler.Top:=(-FDesignedForm.Form.Top)-(FDesignedForm.PositionDelta.y+ifthen(FVerticalScrollPos-SizerLineWidth>0,FVerticalScrollPos-SizerLineWidth,0));
pFormHandler.Width:=(FDesignedForm.Form.Width+abs(FDesignedForm.Form.Left)+FDesignedForm.PositionDelta.x);;
pFormHandler.Height:=(FDesignedForm.Form.Height+abs(FDesignedForm.Form.Top)+FDesignedForm.PositionDelta.y);
end;
function TBasicResizeFrame.GetBackgroundMargin(const AIndex: Integer): Integer;
begin
if FBackground = nil then
Result := 0
else
Result := FBackground.GetMargin(AIndex);
if (AIndex = 1) and HasMainMenu then
Result := Result + GetMenuHeight;
end;
function TBasicResizeFrame.GetNewSize: TPoint;
begin
Result := TPoint.Create(FLastClientWidth,FLastClientHeight);
end;
function TBasicResizeFrame.GetFormHandler: TPanel;
begin
Result := pFormHandler;
end;
function TBasicResizeFrame.GetNodePositioning: Boolean;
begin
Result := FNodePositioning;
end;
function TBasicResizeFrame.GetDesignedForm: IDesignedForm;
begin
Result := FDesignedForm;
end;
procedure TBasicResizeFrame.SetDesignedForm(const AValue: IDesignedForm);
begin
FDesignedForm := AValue;
if FDesignedForm = nil then
begin
if Assigned(FBackground) then
FBackground.ResizeFrame := nil;
FBackground := nil;
end
else
if Supports(FDesignedForm, IDesignedFormBackground, FBackground) then
begin
FBackground.Parent := pBG;
FBackground.ResizeFrame := Self;
end;
// special for QT (at start "design form" has wrong position)
TryBoundDesignedForm;
end;
function TBasicResizeFrame.GetMenuHeight: Integer;
begin
// some WS (Gtk2) return too big SM_CYMENU, just set it according to font height
// no problem, it is used only for the fake main menu
{$IFDEF LCLWin32}
Result := lclintf.GetSystemMetrics(SM_CYMENU);
{$ELSE}
if pBG.HandleAllocated then
Result := pBG.Canvas.TextHeight('Hg') * 4 div 3
else
Result := 20;
{$ENDIF}
end;
procedure TBasicResizeFrame.TryBoundDesignedForm;
begin
if DesignedForm = nil then
Exit;
HideSizeControls;
ShowSizeControls;
// for GTK2 resizing form (pClient is hidden under pBG)
{$IF DEFINED(LCLGtk2) OR DEFINED(LCLQt) OR DEFINED(LCLQt5)}
pFormHandler.SendToBack; // <--- this is a must.
{$ENDIF}
pFormHandler.BringToFront;
pFakeMenu.Visible := HasMainMenu;
if pFakeMenu.Visible then
begin
pFakeMenu.Height := GetMenuHeight;
pFakeMenu.BorderSpacing.Left := BgLeftMargin;
pFakeMenu.BorderSpacing.Top := BgTopMargin - pFakeMenu.Height;
pFakeMenu.BorderSpacing.Right := BgRightMargin;
pFakeMenu.BringToFront;
end;
end;
procedure TBasicResizeFrame.BeginFormSizeUpdate(Sender: TObject);
begin
FLastDesignedWidthToScroll:=DesignedWidthToScroll;
FLastDesignedHeightToScroll:=DesignedHeightToScroll;
pBG.OnPaint := nil;
pBG.SendToBack;
FDesignedForm.BeginUpdate;
end;
procedure TBasicResizeFrame.EndFormSizeUpdate(Sender: TObject);
begin
FDesignedForm.EndUpdate;
pBG.OnPaint := pBGPaint;
end;
function TBasicResizeFrame.GetFrame: TCustomFrame;
begin
Result := Self;
end;
function TBasicResizeFrame.GetVerticalScrollPos: Integer;
begin
Result := FVerticalScrollPos;
end;
procedure TBasicResizeFrame.SetVerticalScrollPos(AValue: Integer);
begin
FVerticalScrollPos := AValue;
end;
function TBasicResizeFrame.GetHorizontalScrollPos: Integer;
begin
Result := FHorizontalScrollPos;
end;
procedure TBasicResizeFrame.SetHorizontalScrollPos(AValue: Integer);
begin
FHorizontalScrollPos := AValue;
end;
function TBasicResizeFrame.GetSizerRectSize: Integer;
begin
Result := SizerRectSize;
end;
function TBasicResizeFrame.GetSizerLineWidth: Integer;
begin
Result := SizerLineWidth;
end;
function TBasicResizeFrame.GetBackgroundPanel: TPanel;
begin
Result := pBG;
end;
function TBasicResizeFrame.DesignedWidthToScroll: Integer;
begin
if DesignedForm = nil then
Exit(0);
if FNodePositioning then
Result := FLastDesignedWidthToScroll
else
Result := abs(DesignedForm.Width - FLastClientWidth);
//Result := DesignedForm.Width - DesignedForm.RealWidth;
end;
procedure TBasicResizeFrame.DesignerSetFocus;
begin
if FFakeFocusControl.CanSetFocus then
FFakeFocusControl.SetFocus;
end;
function TBasicResizeFrame.DesignedHeightToScroll: Integer;
begin
if DesignedForm = nil then
Exit(0);
if FNodePositioning then
Result := FLastDesignedHeightToScroll
else
Result := abs(DesignedForm.Height - FLastClientHeight);
//Result := DesignedForm.Height - DesignedForm.RealHeight;
end;
procedure TBasicResizeFrame.ClientChangeBounds;
begin
ClientChangeBounds(nil);
end;
{}
constructor TBasicResizeFrame.Create(TheOwner: TComponent);
begin
inherited Create(TheOwner);
// Michl: Don't change DesignTimePPI of BasicResizeFrame (sparta_basicresizeframe.lfm).
// There always has to be the default (none entry = 96 PPI) value!
FSizerRectSize := ScaleX(SIZER_RECT_SIZE, 96);
FSizerLineWidth := ScaleX(SIZER_LINE_WIDTH, 96);
FFakeFocusControl := TEdit.Create(Self);
FFakeFocusControl.Parent := Self;
FFakeFocusControl.Top := -100;
FFakeFocusControl.OnKeyDown := FakeKeyDown;
FFakeFocusControl.OnKeyUp := FakeKeyUp;
FFakeFocusControl.OnUTF8KeyPress := FakeUTF8KeyPress;
FFakeFocusControl.OnEnter := FakeExitEnter;
FFakeFocusControl.OnExit := FakeExitEnter;
FNodes := TObjectList.Create(False);
CreateNodes;
pL.OnPaint := PanelPaint;
pT.OnPaint := PanelPaint;
pR.OnPaint := PanelPaint;
pB.OnPaint := PanelPaint;
pClient.OnChangeBounds := ClientChangeBounds;
pBG.OnChangeBounds := BGChangeBounds;
PositionNodes(Self);
Application.AddOnIdleHandler(AppOnIdle);
end;
procedure TBasicResizeFrame.AppOnIdle(Sender: TObject; var Done: Boolean);
var
aHasMainMenu: Boolean;
begin
if FDesignerModified then
begin
aHasMainMenu := HasMainMenu;
if aHasMainMenu <> FOldHasMainMenu then
begin
FOldHasMainMenu := aHasMainMenu;
TryBoundDesignedForm;
if Assigned(OnNodePositioning) then
OnNodePositioning(Self, [pkBottom], pcPositioningEnd);
Application.NotifyUserInputHandler(Self, 0); // force repaint invisible components
end else
if pFakeMenu.Visible then
pFakeMenu.Invalidate; // always repaint menu on modification
FDesignerModified := False;
end;
end;
destructor TBasicResizeFrame.Destroy;
begin
Pointer(FDesignedForm) := nil;
Pointer(FBackground) := nil;
Application.RemoveOnIdleHandler(AppOnIdle);
FNodes.Free;
inherited Destroy;
end;
procedure TBasicResizeFrame.FakeExitEnter(Sender: TObject);
begin
pL.Repaint;
pT.Repaint;
pR.Repaint;
pB.Repaint;
end;
procedure TBasicResizeFrame.FakeKeyDown(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
LWndProc: TWndMethod;
LMsg: TLMKeyUp;
begin
LWndProc := FDesignedForm.Form.WindowProc;
FillChar(LMsg{%H-}, SizeOf(LMsg), 0);
LMsg.msg := CN_KEYDOWN;
LMsg.CharCode := Key;
LWndProc(TLMessage(LMsg));
Key := LMsg.CharCode;
end;
procedure TBasicResizeFrame.FakeKeyUp(Sender: TObject; var Key: Word;
Shift: TShiftState);
var
LWndProc: TWndMethod;
LMsg: TLMKeyUp;
begin
LWndProc := FDesignedForm.Form.WindowProc;
FillChar(LMsg{%H-}, SizeOf(LMsg), 0);
LMsg.msg := CN_KEYUP;
LMsg.CharCode := Key;
LWndProc(TLMessage(LMsg));
Key := LMsg.CharCode;
end;
procedure TBasicResizeFrame.FakeUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char
);
begin
FDesignedForm.Form.IntfUTF8KeyPress(UTF8Key, 1, False);
end;
procedure TBasicResizeFrame.PositionNodes(AroundControl: TWinControl);
var
Node,T,L,CT,CL,FR,FB,FT,FL: Integer;
TopLeft: TPoint;
begin
if FDesignedForm = nil then
Exit;
// positions of bars
if not FNodePositioning then
begin
pL.Left := -FHorizontalScrollPos;
pR.Left := FDesignedForm.Width - FHorizontalScrollPos + pL.Width + BgRightMargin + BgLeftMargin;
pT.Top := -FVerticalScrollPos;
pB.Top := FDesignedForm.Height - FVerticalScrollPos + pT.Height + BgBottomMargin + BgTopMargin;
// width and height
pL.Top:=0;
pL.Height := FDesignedForm.Height + 2*SizerRectSize + BgTopMargin + BgBottomMargin;
pR.Top:=0;
pR.Height := FDesignedForm.Height + 2*SizerRectSize + BgTopMargin + BgBottomMargin;
pT.Left:=0;
pT.Width := FDesignedForm.Width + 2*SizerRectSize + BgLeftMargin + BgRightMargin;
pB.Left:=0;
pB.Width := FDesignedForm.Width + 2*SizerRectSize + BgLeftMargin + BgRightMargin;
// client
if pBG.Left + BgLeftMargin <= 0 then
pClient.Left := -(pBG.Left) - (FHorizontalScrollPos - SizerRectSize)
else
pClient.Left := pBG.Left + BgLeftMargin;
if pBG.Top + BgTopMargin <= 0 then
pClient.Top := -(pBG.Top) - (FVerticalScrollPos - SizerRectSize)
else
pClient.Top := pBG.Top + BgTopMargin;
pClient.Height := Height - pClient.Top - Max(Height - (pB.Top - BgBottomMargin), 0);
pClient.Width := Width - pClient.Left - Max(Width - (pR.Left - BgRightMargin), 0);
end;
AdjustFormHandler;
for Node := 0 to 7 do
begin
with AroundControl do
begin
FR := Width - RightSizerRectWidth - RightMargin;
FB := Height - BottomSizerRectHeight - BottomMargin;
FT := TopSizerRectTop;
FL := LeftSizerRectLeft;
CL := (FR - FL) div 2 + FL;
CT := (FB - FT) div 2 + FT;
case Node of
0: begin
T := FT;
L := FL;
end;
1: begin
T := FT;
L := CL;
end;
2: begin
T := FT;
L := FR;
end;
3: begin
T := CT;
L := FR;
end;
4: begin
T := FB;
L := FR;
end;
5: begin
T := FB;
L := CL;
end;
6: begin
T := FB;
L := FL;
end;
7: begin
T := CT;
L := FL;
end;
else
T := 0;
L := 0;
end;
TopLeft := (Classes.Point(L,T));
end;
with TPanel(FNodes[Node]) do
begin
Top := TopLeft.Y;
Left := TopLeft.X;
Repaint;
end;
end;
end;
end.