mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-05-25 21:42:39 +02:00
1148 lines
31 KiB
ObjectPascal
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.
|
|
|