1 unit sparta_BasicResizeFrame;
2
3 {$mode delphi}{$H+}
4
5 interface
6
7 uses
8 Classes, contnrs, SysUtils, FileUtil, Forms, Controls, ExtCtrls, StdCtrls,
9 Graphics, LCLType, lclintf, Menus, LMessages, Math, Types, sparta_InterfacesMDI;
10
11 type
12 TPositioningCode = (pcPositioning, pcPositioningEnd);
13 TPositioningKind = set of (pkBottom, pkRight);
14 TPositioningEvent = procedure(Sender: TObject; PositioningKind: TPositioningKind; PositioningCode: TPositioningCode) of object;
15
16 { TBasicResizeFrame }
17
18 TResizerFrameClass = class of TBasicResizeFrame;
19 TBasicResizeFrame = class(TFrame, IResizeFrame)
20 iResizerLineImg: TImage;
21 pFormHandler: TPanel;
22 pFakeMenu: TPanel;
23 pBG: TPanel;
24 pB: TPanel;
25 pClient: TPanel;
26 pL: TPanel;
27 pMarginB: TPanel;
28 pMarginL: TPanel;
29 pMarginR: TPanel;
30 pMarginT: TPanel;
31 pR: TPanel;
32 pT: TPanel;
33 procedure pBGPaint(Sender: TObject);
34 procedure pFakeMenuPaint(Sender: TObject);
35 procedure sbVerticalScroll(Sender: TObject; ScrollCode: TScrollCode;
36 var ScrollPos: Integer);
37 procedure sbHorizontalScroll(Sender: TObject; ScrollCode: TScrollCode;
38 var ScrollPos: Integer);
39 public const
40 SIZER_RECT_SIZE = 8;
41 SIZER_LINE_WIDTH = 8;
42 private
43 FVerticalScrollPos: Integer;
44 FHorizontalScrollPos: Integer;
45 FDesignedForm: IDesignedForm;
46 FBackground: IDesignedFormBackground;
47 FFakeFocusControl: TWinControl;
48
49 procedure FakeExitEnter(Sender: TObject);
50 procedure FakeKeyDown(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
51 procedure FakeKeyUp(Sender: TObject; var Key: Word; {%H-}Shift: TShiftState);
52 procedure FakeUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
53 private
54 { private declarations }
55 FOnNodePositioning: TPositioningEvent;
56 FOnHorizontalScroll, FOnVerticalScroll: TScrollEvent;
57 FLastRightMarign: Integer;
58 FLastBottomMarign: Integer;
59 FNodePositioning: Boolean;
60 FOldPos, FDelta: TPoint;
61 FPositioningKind: TPositioningKind;
62 FMaxWidth, FMaxHeight: Integer;
63 FLastClientWidth, FLastClientHeight: Integer;
64 FLastDesignedWidthToScroll, FLastDesignedHeightToScroll: Integer;
65 FOldHasMainMenu: Boolean;
66 FDesignerModified: Boolean;
67 FSizerLineWidth: Integer;
68 FSizerRectSize: Integer;
69
HasMainMenunull70 function HasMainMenu: Boolean;
71 procedure AppOnIdle(Sender: TObject; var {%H-}Done: Boolean);
72
73 procedure PanelPaint(Sender: TObject);
74 procedure BGChangeBounds(Sender: TObject);
75
76 procedure CreateNodes;
77 procedure NodeMouseDown(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
78 procedure NodeMouseMove(Sender: TObject; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
79 procedure NodeMouseUp(Sender: TObject; {%H-}Button: TMouseButton; {%H-}Shift: TShiftState; {%H-}X, {%H-}Y: Integer);
80
GetRightMarginnull81 function GetRightMargin: Integer;
GetBottomMarginnull82 function GetBottomMargin: Integer;
83
84 // dependent on scroll position
85 // for Vertical
BottomSizerRectHeightnull86 function BottomSizerRectHeight: Integer;
BottomSizerLineWidthnull87 function BottomSizerLineWidth: Integer;
TopSizerRectTopnull88 function TopSizerRectTop: Integer;
TopSizerLineWidthnull89 function TopSizerLineWidth: Integer;
VerticalSizerLineLengthnull90 function VerticalSizerLineLength: Integer;
91 // for Horizontal
RightSizerRectWidthnull92 function RightSizerRectWidth: Integer;
RightSizerLineWidthnull93 function RightSizerLineWidth: Integer;
LeftSizerRectLeftnull94 function LeftSizerRectLeft: Integer;
LeftSizerLineWidthnull95 function LeftSizerLineWidth: Integer;
HorizontalSizerLineLengthnull96 function HorizontalSizerLineLength: Integer;
97
98 procedure AdjustFormHandler;
99
GetMenuHeightnull100 function GetMenuHeight: Integer;
101 protected
102 FNodes: TObjectList;
103 protected
104 procedure TryBoundDesignedForm; virtual;
105 procedure BeginFormSizeUpdate(Sender: TObject); virtual;
106 procedure EndFormSizeUpdate(Sender: TObject); virtual;
107 protected { IResizeFrame }
108 procedure HideSizeRects;
109 procedure ShowSizeRects;
110 procedure PositionNodes; overload;
DesignedWidthToScrollnull111 function DesignedWidthToScroll: Integer;
DesignedHeightToScrollnull112 function DesignedHeightToScroll: Integer;
113 procedure ClientChangeBounds; overload;
114
GetFramenull115 function GetFrame: TCustomFrame;
GetVerticalScrollPosnull116 function GetVerticalScrollPos: Integer;
117 procedure SetVerticalScrollPos(AValue: Integer);
GetHorizontalScrollPosnull118 function GetHorizontalScrollPos: Integer;
119 procedure SetHorizontalScrollPos(AValue: Integer);
120
GetBackgroundPanelnull121 function GetBackgroundPanel: TPanel;
GetBackgroundMarginnull122 function GetBackgroundMargin(const AIndex: Integer): Integer;
123
GetNewSizenull124 function GetNewSize: TPoint;
GetFormHandlernull125 function GetFormHandler: TPanel;
GetNodePositioningnull126 function GetNodePositioning: Boolean;
GetDesignedFormnull127 function GetDesignedForm: IDesignedForm;
128 procedure SetDesignedForm(const AValue: IDesignedForm);
129
GetSizerRectSizenull130 function GetSizerRectSize: Integer;
GetSizerLineWidthnull131 function GetSizerLineWidth: Integer;
132 public { IResizeFrame }
133 procedure DesignerSetFocus;
134 public
135 constructor Create(TheOwner: TComponent); override;
136 destructor Destroy; override;
137
138 property DesignedForm: IDesignedForm read GetDesignedForm write SetDesignedForm;
139
140 procedure PositionNodes(AroundControl: TWinControl); overload;
141 property NodePositioning: Boolean read GetNodePositioning;
142 procedure ClientChangeBounds(Sender: TObject); overload;
143
144 property RightMargin: Integer read GetRightMargin;
145 property BottomMargin: Integer read GetBottomMargin;
146 property OnNodePositioning: TPositioningEvent read FOnNodePositioning write FOnNodePositioning;
147
148 property BgLeftMargin: Integer index 0 read GetBackgroundMargin;
149 property BgTopMargin: Integer index 1 read GetBackgroundMargin;
150 property BgRightMargin: Integer index 2 read GetBackgroundMargin;
151 property BgBottomMargin: Integer index 3 read GetBackgroundMargin;
152 property SizerRectSize: Integer read FSizerRectSize;
153 property SizerLineWidth: Integer read FSizerLineWidth;
154
155 procedure HideSizeControls;
156 procedure ShowSizeControls;
157
158 procedure OnModified;
159
160 property VerticalScrollPos: Integer read GetVerticalScrollPos write SetVerticalScrollPos;
161 property HorizontalScrollPos: Integer read GetHorizontalScrollPos write SetHorizontalScrollPos;
162 end;
163
164 implementation
165
166 {$R *.lfm}
167
168 { Node grip indices are as follows:
169
170 1
171 0 +----+----+ 2
172 | |
173 7 + + 3
174 | |
175 6 +----+----+ 4
176 5
177
178 Only grips 3, 4, and 5 are sizeable }
179
180 procedure TBasicResizeFrame.pFakeMenuPaint(Sender: TObject);
181 var
182 MenuRect: Types.TRect;
183 Menu: TMainMenu;
184 X, Y, I: Integer;
185 LCanvas: TCanvas;
186 begin
187 //fake paint menu
188
189 MenuRect := pFakeMenu.ClientRect;
190 LCanvas := pFakeMenu.Canvas;
191 LCanvas.Brush.Color := clMenuBar;
192 LCanvas.FillRect(MenuRect);
193
194 // pFakeMenu is visible only when HasMainMenu is true
195 // but FDesignedForm can be nil if the designer is painted before it has been assigned
196 if not HasMainMenu then
197 Exit;
198
199 Menu := FDesignedForm.Form.Menu;
200 LCanvas.Font.Color := clMenuText;
201
202 X := 5;
203 Y := (MenuRect.Top+MenuRect.Bottom-LCanvas.TextHeight('Hg')) div 2;
204 for I := 0 to Menu.Items.Count-1 do
205 if Menu.Items[I].Visible then
206 begin
207 LCanvas.TextOut(X, Y, Menu.Items[I].Caption);
208 Inc(X, LCanvas.TextWidth(Menu.Items[I].Caption) + 10);
209 end;
210 end;
211
212 procedure TBasicResizeFrame.pBGPaint(Sender: TObject);
213 begin
214 pBG.SendToBack;
215 end;
216
217 procedure TBasicResizeFrame.sbVerticalScroll(Sender: TObject;
218 ScrollCode: TScrollCode; var ScrollPos: Integer);
219 begin
220 if ScrollCode <> scEndScroll then
221 HideSizeRects
222 else
223 ShowSizeRects;
224
225 FVerticalScrollPos := ScrollPos;
226
227 PositionNodes(Self);
228
229 if Assigned(FOnVerticalScroll)
230 // for refresh from this class, pass sender as nil.
231 // In other case program will go into infinity loop
232 and (Sender <> nil) then
233 FOnVerticalScroll(Sender, ScrollCode, ScrollPos);
234 end;
235
236 procedure TBasicResizeFrame.sbHorizontalScroll(Sender: TObject;
237 ScrollCode: TScrollCode; var ScrollPos: Integer);
238 begin
239 if ScrollCode <> scEndScroll then
240 HideSizeRects
241 else
242 ShowSizeRects;
243
244 FHorizontalScrollPos := ScrollPos;
245
246 PositionNodes(Self);
247
248 if Assigned(FOnHorizontalScroll)
249 // for refresh from this class, pass sender as nil.
250 // In other case program will go into infinity loop
251 and (Sender <> nil) then
252 FOnHorizontalScroll(Sender, ScrollCode, ScrollPos);
253 end;
254
255 { TResizerFrame }
256
257 // Tiles the source image over the given target canvas
258 procedure TileImage(const ASource: TImage; ATarget: TCanvas; AX, AY,
259 AWidth, AHeight: Integer);
260 var
261 LX, LY, LDeltaX, LDeltaY: Integer;
262 begin
263 LDeltaX := ASource.Width;
264 LDeltaY := ASource.Height;
265 LY := 0;
266 while LY < AHeight do
267 begin
268 LX := 0;
269 while LX < AWidth do
270 begin
271 ATarget.Draw(AX + LX, AY + LY, ASource.Picture.graphic);
272 Inc(LX, LDeltaX);
273 end;
274 Inc(LY, LDeltaY);
275 end;
276 end;
277
278 procedure TBasicResizeFrame.PanelPaint(Sender: TObject);
279 var
280 LWidth, LHeight: Integer;
281 LOldColor: TColor;
282 LCanvas: TCanvas;
283 begin
284 if FNodePositioning then
285 Exit;
286 if (Sender = pR) or (Sender = pL) then
287 begin
288 LWidth := SizerLineWidth;
289 LHeight := Height;
290 end else
291 begin
292 LWidth := Width;
293 LHeight := SizerLineWidth;
294 end;
295 LCanvas := (Sender as TPanel).Canvas;
296 if FFakeFocusControl.Focused then
297 begin
298 LOldColor := LCanvas.Brush.Color;
299 LCanvas.Brush.Color := $FFEEDD;
300 LCanvas.FillRect(0, 0, LWidth, LHeight);
301 LCanvas.Brush.Color := LOldColor;
302 end;
303 TileImage(iResizerLineImg, LCanvas, 0, 0, LWidth, LHeight);
304 end;
305
306 procedure TBasicResizeFrame.ClientChangeBounds(Sender: TObject);
307 begin
308 if (DesignedForm = nil) or FNodePositioning then
309 Exit;
310
311 FLastClientWidth := pClient.Width;
312 FLastClientHeight := pClient.Height;
313
314 (*
315 DesignedForm.BeginUpdate;
316
317 DesignedForm.RealLeft := 0;
318 DesignedForm.RealTop := 0;
319 DesignedForm.RealWidth := pClient.Width;
320 DesignedForm.RealHeight := pClient.Height;
321 DesignedForm.EndUpdate;
322 *)
323 end;
324
325 procedure TBasicResizeFrame.BGChangeBounds(Sender: TObject);
326 begin
327 PositionNodes(Self);
328 end;
329
330 procedure TBasicResizeFrame.HideSizeRects;
331 var
332 p: TObject;
333 wc: TWinControl absolute p;
334 begin
335 for p in FNodes do
336 if not (wc is TPanel) then
337 wc.Visible := False;
338 end;
339
340 procedure TBasicResizeFrame.HideSizeControls;
341 begin
342 pL.Repaint;
343 pT.Repaint;
344 pR.Repaint;
345 pB.Repaint;
346
347 HideSizeRects;
348 pBG.SendToBack;
349 end;
350
351 procedure TBasicResizeFrame.ShowSizeRects;
352 var
353 p: TObject;
354 wc: TWinControl absolute p;
355 begin
356 for p in FNodes do
357 wc.Visible := True;
358 end;
359
360 procedure TBasicResizeFrame.PositionNodes;
361 begin
362 PositionNodes(Self);
363 end;
364
365 procedure TBasicResizeFrame.ShowSizeControls;
366 begin
367 pL.Repaint;
368 pT.Repaint;
369 pR.Repaint;
370 pB.Repaint;
371
372 ShowSizeRects;
373 //pBG.Visible := True;
374 end;
375
376 procedure TBasicResizeFrame.CreateNodes;
377 var
378 Node: Integer;
379 Panel: TPanel;
380 begin
381 for Node := 0 to 7 do
382 begin
383 Panel := TPanel.Create(self);
384 with Panel do
385 begin
386 BevelOuter := bvNone;
387 Color := clBlack;
388
389 Name := 'Node' + IntToStr(Node);
390 Caption:='';
391 Width := SIZER_RECT_SIZE; // scaled dynamically by LCL
392 Height := SIZER_RECT_SIZE; // scaled dynamically by LCL
393 Parent := Self;
394 Visible := True;
395 FNodes.Add(Panel);
396
397 case Node of
398 // on mac there is no cursor for crNWSE ( https://bugs.freepascal.org/view.php?id=32194#c101876 )
399 {0,}4: Cursor := {$IFDEF MACOS}crSizeAll{$ELSE}crSizeNWSE{$ENDIF};
400 {1,}5: Cursor := crSizeNS;
401 //{2,}6: Cursor := $IFDEF MACOS}crSizeAll{$ELSE}crSizeNESW{$ENDIF};
402 3{,7}: Cursor := crSizeWE;
403 end;
404 if Node in [3,4,5] then
405 begin
406 OnMouseDown := NodeMouseDown;
407 OnMouseMove := NodeMouseMove;
408 OnMouseUp := NodeMouseUp;
409 end;
410
411 with TShape.Create(Panel) do
412 begin
413 Parent := Panel;
414 Align:= alClient;
415 Enabled := False;
416
417 if Node in [3,4,5] then
418 Brush.Color:=clBtnFace
419 else
420 Brush.Color:=clGray;
421 end;
422 end;
423 end;
424 // extra resizers
425 pB.OnMouseDown := NodeMouseDown;
426 pB.OnMouseMove := NodeMouseMove;
427 pB.OnMouseUp := NodeMouseUp;
428
429 pR.OnMouseDown := NodeMouseDown;
430 pR.OnMouseMove := NodeMouseMove;
431 pR.OnMouseUp := NodeMouseUp;
432
433 FNodes.Add(pL);
434 FNodes.Add(pT);
435 FNodes.Add(pR);
436 FNodes.Add(pB);
437 end;
438
439 procedure TBasicResizeFrame.NodeMouseDown(Sender: TObject; Button: TMouseButton;
440 Shift: TShiftState; X, Y: Integer);
441 var
442 LCtrlPoint: TPoint;
443 begin
444 { TShape in TBasicResizeFrame.CreateNodes is disabled, anyway in future can be usefull
445 if Sender is TGraphicControl then
446 Sender := TGraphicControl(Sender).Parent;}
447
448 if (Enabled) and (Sender is TWinControl) then
449 begin
450 FNodePositioning:=True;
451 BeginFormSizeUpdate(Sender);
452
453 // when we start resizing the rules do not apply to us :)
454 FMaxWidth := Constraints.MaxWidth;
455 FMaxHeight := Constraints.MaxHeight;
456 Constraints.MaxWidth := 0;
457 Constraints.MaxHeight := 0;
458 with pClient do
459 begin
460 Align := alClient;
461 if pBG.Left + BgLeftMargin <= 0 then
462 BorderSpacing.Left := Max(-pBG.Left - (FHorizontalScrollPos - SizerRectSize), 0)
463 else
464 BorderSpacing.Left := Max(pBG.Left + BgLeftMargin, 0);
465
466 if pBG.Top + BgTopMargin <= 0 then
467 BorderSpacing.Top := Max(-pBG.Top - (FVerticalScrollPos - SizerRectSize), 0)
468 else
469 BorderSpacing.Top := Max(pBG.Top + BgTopMargin, 0);
470
471 BorderSpacing.Right := Max(Self.Width - (pR.Left - BgRightMargin), 0);
472 BorderSpacing.Bottom := Max(Self.Height - (pB.Top - BgBottomMargin), 0);
473 end;
474
475
476 {$IF Defined(LCLWin32) or Defined(LCLWin64)}
477 SetCapture(TWinControl(Sender).Handle);
478 {$ENDIF}
479 GetCursorPos(FOldPos);
480 // perform first "click delta" to reduce leap
481 // + calculate delta created by scrollbars and theirs position...
482 FillChar(FDelta, SizeOf(FDelta), #0);
483 LCtrlPoint := (Sender as TWinControl).ScreenToClient(Mouse.CursorPos);
484 if Sender = pR then
485 begin
486 FDelta.X := -(LCtrlPoint.x - RightSizerLineWidth) + RightMargin + Left;
487 FPositioningKind := [pkRight];
488 end
489 else if Sender = pB then
490 begin
491 FDelta.Y := -(LCtrlPoint.y - BottomSizerLineWidth) + BottomMargin + Top;
492 FPositioningKind := [pkBottom];
493 end
494 else
495 case FNodes.IndexOf(Sender) of
496 3: // middle right
497 begin
498 FDelta.X := -(LCtrlPoint.x - RightSizerRectWidth) + RightMargin + Left;
499 FPositioningKind := [pkRight];
500 end;
501 4: // right bottom
502 begin
503 FDelta.X := -(LCtrlPoint.x - RightSizerRectWidth) + RightMargin + Left;
504 FDelta.Y := -(LCtrlPoint.y - BottomSizerRectHeight) + BottomMargin + Top;
505 FPositioningKind := [pkRight, pkBottom];
506 end;
507 5: // middle bottom
508 begin
509 FDelta.Y := -(LCtrlPoint.y - BottomSizerRectHeight) + BottomMargin + Top;
510 FPositioningKind := [pkBottom];
511 end;
512 end;
513 end;
514 end;
515
516 procedure TBasicResizeFrame.NodeMouseMove(Sender: TObject; Shift: TShiftState; X,
517 Y: Integer);
518 var
519 newPos: TPoint;
520 frmPoint : TPoint;
521 OldRect: TRect;
522 AdjL,AdjR,AdjT,AdjB: Boolean;
523 begin
524 { TShape in TBasicResizeFrame.CreateNodes is disabled, anyway in future can be usefull
525 // handle TPanel for resizing rectangles
526 if Sender is TGraphicControl then
527 Sender := TGraphicControl(Sender).Parent;}
528
529 if FNodePositioning then
530 begin
531 with TWinControl(Sender) do
532 begin
533 newPos := Point(0, 0);
534 GetCursorPos(newPos);
535
536 if (newPos.x = FOldPos.x) and (newPos.y = FOldPos.y) then
537 Exit;
538
539 HideSizeControls;
540 UpdateWindow(pBG.Handle);
541 UpdateWindow(Self.Handle);
542 UpdateWindow(Self.Parent.Handle);
543 with Self do
544 begin //resize
545 frmPoint := Self.ScreenToClient(Mouse.CursorPos);
546 frmPoint.x:= frmPoint.x + FDelta.x;
547 frmPoint.y:= frmPoint.y + FDelta.y;
548
549 OldRect := Self.BoundsRect;
550 AdjL := False;
551 AdjR := False;
552 AdjT := False;
553 AdjB := False;
554 case FNodes.IndexOf(TWinControl(Sender)) of
555 0: begin
556 //AdjL := True;
557 //AdjT := True;
558 end;
559 1: begin
560 //AdjT := True;
561 end;
562 2: begin
563 //AdjR := True;
564 //AdjT := True;
565 end;
566 3, 10: begin
567 AdjR := True;
568 end;
569 4: begin
570 AdjR := True;
571 AdjB := True;
572 end;
573 5, 11: begin
574 AdjB := True;
575 end;
576 6: begin
577 //AdjL := True;
578 //AdjB := True;
579 end;
580 7: begin
581 //AdjL := True;
582 end;
583 end;
584
585 if AdjL then
586 OldRect.Left := frmPoint.X;
587 if AdjR then
588 OldRect.Right := frmPoint.X;
589 if AdjT then
590 OldRect.Top := frmPoint.Y;
591 if AdjB then
592 OldRect.Bottom := frmPoint.Y;
593
594 SetBounds(OldRect.Left,OldRect.Top,OldRect.Right - OldRect.Left,OldRect.Bottom - OldRect.Top);
595 end;
596 //move node
597 Left := Left - FOldPos.X + newPos.X;
598 Top := Top - FOldPos.Y + newPos.Y;
599 FOldPos := newPos;
600 end;
601 PositionNodes(Self);
602 if Assigned(OnNodePositioning) then
603 OnNodePositioning(Self, FPositioningKind, pcPositioning);
604
605 // the same operation as belowe exist in ClientChangeBounds but it is
606 // disabled for FNodePositioning = true
607 // we need to refresh this values after OnNodePositioning
608 FLastClientWidth := pClient.Width;
609 FLastClientHeight:= pClient.Height;
610 end;
611 end;
612
613 procedure TBasicResizeFrame.NodeMouseUp(Sender: TObject; Button: TMouseButton;
614 Shift: TShiftState; X, Y: Integer);
615 begin
616 { TShape in TBasicResizeFrame.CreateNodes is disabled, anyway in future can be usefull
617 if Sender is TGraphicControl then
618 Sender := TGraphicControl(Sender).Parent;}
619
620 if FNodePositioning then
621 begin
622 Screen.Cursor := crDefault;
623 {$IF Defined(LCLWin32) or Defined(LCLWin64)}
624 ReleaseCapture;
625 {$ENDIF}
626
627 Constraints.MaxWidth := FMaxWidth;
628 Constraints.MaxHeight := FMaxHeight;
629 FNodePositioning := False;
630 ShowSizeControls;
631 if Assigned(OnNodePositioning) then
632 OnNodePositioning(Sender, FPositioningKind, pcPositioningEnd);
633 FPositioningKind := [];
634 FNodePositioning := False;
635
636 pClient.Align := alNone;
637 BorderSpacing.Left := 0;
638 BorderSpacing.Top := 0;
639 BorderSpacing.Right := 0;
640 BorderSpacing.Bottom := 0;
641 PositionNodes(Self);
642
643 EndFormSizeUpdate(Sender);
644
645 // after resizing, TFrame is frozen in Windows OS
646 // this is trick to workaraund IDE bug. Also for proper size for normal form
647 TryBoundDesignedForm;
648 // for small resizes, designed form is moved on the top and on the bottom
649 // is showed white block - to stop this we need to move pClient to right position
650 PositionNodes;
651 ShowSizeControls;
652 end;
653 end;
654
655 procedure TBasicResizeFrame.OnModified;
656 begin
657 FDesignerModified := True;
658 end;
659
TBasicResizeFrame.GetRightMarginnull660 function TBasicResizeFrame.GetRightMargin: Integer;
661 begin
662 if not FNodePositioning then
663 FLastRightMarign := Width - (pR.Left + pR.Width);
664 Result := FLastRightMarign;
665 end;
666
HasMainMenunull667 function TBasicResizeFrame.HasMainMenu: Boolean;
668 var
669 I: Integer;
670 begin
671 Result := False;
672 if (FDesignedForm<>nil) and (FDesignedForm.Form.Menu<>nil)
673 and not (csDestroying in FDesignedForm.Form.Menu.ComponentState)
674 and (FDesignedForm.Form.Menu.Items.Count>0)
675 then
676 for I := 0 to FDesignedForm.Form.Menu.Items.Count-1 do
677 if FDesignedForm.Form.Menu.Items[I].Visible then
678 Exit(True);
679 end;
680
TBasicResizeFrame.GetBottomMarginnull681 function TBasicResizeFrame.GetBottomMargin: Integer;
682 begin
683 if not FNodePositioning then
684 FLastBottomMarign := Height - (pB.Top + pB.Height);
685 Result := FLastBottomMarign;
686 end;
687
688 {-----------------------------------------------------------------------------------------------------------------------
689 for Vertical scroll
690 {----------------------------------------------------------------------------------------------------------------------}
691
BottomSizerRectHeightnull692 function TBasicResizeFrame.BottomSizerRectHeight: Integer;
693 begin
694 Result := SizerRectSize;
695 end;
696
TBasicResizeFrame.BottomSizerLineWidthnull697 function TBasicResizeFrame.BottomSizerLineWidth: Integer;
698 begin
699 Result := SizerLineWidth;
700 end;
701
TBasicResizeFrame.TopSizerRectTopnull702 function TBasicResizeFrame.TopSizerRectTop: Integer;
703 begin
704 Result := -FVerticalScrollPos;
705 end;
706
TopSizerLineWidthnull707 function TBasicResizeFrame.TopSizerLineWidth: Integer;
708 begin
709 Result := SizerLineWidth;
710 end;
711
VerticalSizerLineLengthnull712 function TBasicResizeFrame.VerticalSizerLineLength: Integer;
713 begin
714 Result := Height - BottomMargin;
715 end;
716
717 {-----------------------------------------------------------------------------------------------------------------------
718 for Horizontal scroll
719 {----------------------------------------------------------------------------------------------------------------------}
720
TBasicResizeFrame.RightSizerRectWidthnull721 function TBasicResizeFrame.RightSizerRectWidth: Integer;
722 begin
723 Result := SizerRectSize;
724 end;
725
RightSizerLineWidthnull726 function TBasicResizeFrame.RightSizerLineWidth: Integer;
727 begin
728 Result := SizerLineWidth;
729 end;
730
TBasicResizeFrame.LeftSizerRectLeftnull731 function TBasicResizeFrame.LeftSizerRectLeft: Integer;
732 begin
733 Result := -FHorizontalScrollPos;
734 end;
735
LeftSizerLineWidthnull736 function TBasicResizeFrame.LeftSizerLineWidth: Integer;
737 begin
738 Result := SizerLineWidth;
739 end;
740
HorizontalSizerLineLengthnull741 function TBasicResizeFrame.HorizontalSizerLineLength: Integer;
742 begin
743 Result := Width - RightMargin;
744 end;
745
746 procedure TBasicResizeFrame.AdjustFormHandler;
747 begin
748 pFormHandler.Left:=(-FDesignedForm.Form.Left)-(FDesignedForm.PositionDelta.x+ifthen(FHorizontalScrollPos-SizerLineWidth>0,FHorizontalScrollPos-SizerLineWidth,0));
749 pFormHandler.Top:=(-FDesignedForm.Form.Top)-(FDesignedForm.PositionDelta.y+ifthen(FVerticalScrollPos-SizerLineWidth>0,FVerticalScrollPos-SizerLineWidth,0));
750 pFormHandler.Width:=(FDesignedForm.Form.Width+abs(FDesignedForm.Form.Left)+FDesignedForm.PositionDelta.x);;
751 pFormHandler.Height:=(FDesignedForm.Form.Height+abs(FDesignedForm.Form.Top)+FDesignedForm.PositionDelta.y);
752 end;
753
GetBackgroundMarginnull754 function TBasicResizeFrame.GetBackgroundMargin(const AIndex: Integer): Integer;
755 begin
756 if FBackground = nil then
757 Result := 0
758 else
759 Result := FBackground.GetMargin(AIndex);
760
761 if (AIndex = 1) and HasMainMenu then
762 Result := Result + GetMenuHeight;
763 end;
764
GetNewSizenull765 function TBasicResizeFrame.GetNewSize: TPoint;
766 begin
767 Result := TPoint.Create(FLastClientWidth,FLastClientHeight);
768 end;
769
GetFormHandlernull770 function TBasicResizeFrame.GetFormHandler: TPanel;
771 begin
772 Result := pFormHandler;
773 end;
774
GetNodePositioningnull775 function TBasicResizeFrame.GetNodePositioning: Boolean;
776 begin
777 Result := FNodePositioning;
778 end;
779
GetDesignedFormnull780 function TBasicResizeFrame.GetDesignedForm: IDesignedForm;
781 begin
782 Result := FDesignedForm;
783 end;
784
785 procedure TBasicResizeFrame.SetDesignedForm(const AValue: IDesignedForm);
786 begin
787 FDesignedForm := AValue;
788 if FDesignedForm = nil then
789 begin
790 if Assigned(FBackground) then
791 FBackground.ResizeFrame := nil;
792 FBackground := nil;
793 end
794 else
795 if Supports(FDesignedForm, IDesignedFormBackground, FBackground) then
796 begin
797 FBackground.Parent := pBG;
798 FBackground.ResizeFrame := Self;
799 end;
800 // special for QT (at start "design form" has wrong position)
801 TryBoundDesignedForm;
802 end;
803
GetMenuHeightnull804 function TBasicResizeFrame.GetMenuHeight: Integer;
805 begin
806 // some WS (Gtk2) return too big SM_CYMENU, just set it according to font height
807 // no problem, it is used only for the fake main menu
808
809 {$IFDEF LCLWin32}
810 Result := lclintf.GetSystemMetrics(SM_CYMENU);
811 {$ELSE}
812 if pBG.HandleAllocated then
813 Result := pBG.Canvas.TextHeight('Hg') * 4 div 3
814 else
815 Result := 20;
816 {$ENDIF}
817 end;
818
819 procedure TBasicResizeFrame.TryBoundDesignedForm;
820 begin
821 if DesignedForm = nil then
822 Exit;
823
824 HideSizeControls;
825 ShowSizeControls;
826
827 // for GTK2 resizing form (pClient is hidden under pBG)
828 {$IF DEFINED(LCLGtk2) OR DEFINED(LCLQt) OR DEFINED(LCLQt5)}
829 pFormHandler.SendToBack; // <--- this is a must.
830 {$ENDIF}
831 pFormHandler.BringToFront;
832
833 pFakeMenu.Visible := HasMainMenu;
834 if pFakeMenu.Visible then
835 begin
836 pFakeMenu.Height := GetMenuHeight;
837 pFakeMenu.BorderSpacing.Left := BgLeftMargin;
838 pFakeMenu.BorderSpacing.Top := BgTopMargin - pFakeMenu.Height;
839 pFakeMenu.BorderSpacing.Right := BgRightMargin;
840 pFakeMenu.BringToFront;
841 end;
842 end;
843
844 procedure TBasicResizeFrame.BeginFormSizeUpdate(Sender: TObject);
845 begin
846 FLastDesignedWidthToScroll:=DesignedWidthToScroll;
847 FLastDesignedHeightToScroll:=DesignedHeightToScroll;
848 pBG.OnPaint := nil;
849 pBG.SendToBack;
850 end;
851
852 procedure TBasicResizeFrame.EndFormSizeUpdate(Sender: TObject);
853 begin
854 pBG.OnPaint := pBGPaint;
855 end;
856
GetFramenull857 function TBasicResizeFrame.GetFrame: TCustomFrame;
858 begin
859 Result := Self;
860 end;
861
TBasicResizeFrame.GetVerticalScrollPosnull862 function TBasicResizeFrame.GetVerticalScrollPos: Integer;
863 begin
864 Result := FVerticalScrollPos;
865 end;
866
867 procedure TBasicResizeFrame.SetVerticalScrollPos(AValue: Integer);
868 begin
869 FVerticalScrollPos := AValue;
870 end;
871
GetHorizontalScrollPosnull872 function TBasicResizeFrame.GetHorizontalScrollPos: Integer;
873 begin
874 Result := FHorizontalScrollPos;
875 end;
876
877 procedure TBasicResizeFrame.SetHorizontalScrollPos(AValue: Integer);
878 begin
879 FHorizontalScrollPos := AValue;
880 end;
881
TBasicResizeFrame.GetSizerRectSizenull882 function TBasicResizeFrame.GetSizerRectSize: Integer;
883 begin
884 Result := SizerRectSize;
885 end;
886
GetSizerLineWidthnull887 function TBasicResizeFrame.GetSizerLineWidth: Integer;
888 begin
889 Result := SizerLineWidth;
890 end;
891
GetBackgroundPanelnull892 function TBasicResizeFrame.GetBackgroundPanel: TPanel;
893 begin
894 Result := pBG;
895 end;
896
DesignedWidthToScrollnull897 function TBasicResizeFrame.DesignedWidthToScroll: Integer;
898 begin
899 if DesignedForm = nil then
900 Exit(0);
901 if FNodePositioning then
902 Result := FLastDesignedWidthToScroll
903 else
904 Result := abs(DesignedForm.Width - FLastClientWidth);
905 //Result := DesignedForm.Width - DesignedForm.RealWidth;
906 end;
907
908 procedure TBasicResizeFrame.DesignerSetFocus;
909 begin
910 if FFakeFocusControl.CanSetFocus then
911 FFakeFocusControl.SetFocus;
912 end;
913
TBasicResizeFrame.DesignedHeightToScrollnull914 function TBasicResizeFrame.DesignedHeightToScroll: Integer;
915 begin
916 if DesignedForm = nil then
917 Exit(0);
918
919 if FNodePositioning then
920 Result := FLastDesignedHeightToScroll
921 else
922 Result := abs(DesignedForm.Height - FLastClientHeight);
923 //Result := DesignedForm.Height - DesignedForm.RealHeight;
924 end;
925
926 procedure TBasicResizeFrame.ClientChangeBounds;
927 begin
928 ClientChangeBounds(nil);
929 end;
930
931 {}
932
933 constructor TBasicResizeFrame.Create(TheOwner: TComponent);
934 begin
935 inherited Create(TheOwner);
936
937 // Michl: Don't change DesignTimePPI of BasicResizeFrame (sparta_basicresizeframe.lfm).
938 // There always has to be the default (none entry = 96 PPI) value!
939 FSizerRectSize := ScaleX(SIZER_RECT_SIZE, 96);
940 FSizerLineWidth := ScaleX(SIZER_LINE_WIDTH, 96);
941
942 FFakeFocusControl := TEdit.Create(Self);
943 FFakeFocusControl.Parent := Self;
944 FFakeFocusControl.Top := -100;
945 FFakeFocusControl.OnKeyDown := FakeKeyDown;
946 FFakeFocusControl.OnKeyUp := FakeKeyUp;
947 FFakeFocusControl.OnUTF8KeyPress := FakeUTF8KeyPress;
948 FFakeFocusControl.OnEnter := FakeExitEnter;
949 FFakeFocusControl.OnExit := FakeExitEnter;
950
951 FNodes := TObjectList.Create(False);
952 CreateNodes;
953
954 pL.OnPaint := PanelPaint;
955 pT.OnPaint := PanelPaint;
956 pR.OnPaint := PanelPaint;
957 pB.OnPaint := PanelPaint;
958
959 pClient.OnChangeBounds := ClientChangeBounds;
960 pBG.OnChangeBounds := BGChangeBounds;
961 PositionNodes(Self);
962
963 Application.AddOnIdleHandler(AppOnIdle);
964 end;
965
966 procedure TBasicResizeFrame.AppOnIdle(Sender: TObject; var Done: Boolean);
967 var
968 aHasMainMenu: Boolean;
969 begin
970 if FDesignerModified then
971 begin
972 aHasMainMenu := HasMainMenu;
973 if aHasMainMenu <> FOldHasMainMenu then
974 begin
975 FOldHasMainMenu := aHasMainMenu;
976 TryBoundDesignedForm;
977 if Assigned(OnNodePositioning) then
978 OnNodePositioning(Self, [pkBottom], pcPositioningEnd);
979 Application.NotifyUserInputHandler(Self, 0); // force repaint invisible components
980 end else
981 if pFakeMenu.Visible then
982 pFakeMenu.Invalidate; // always repaint menu on modification
983
984 FDesignerModified := False;
985 end;
986 end;
987
988 destructor TBasicResizeFrame.Destroy;
989 begin
990 Pointer(FDesignedForm) := nil;
991 Pointer(FBackground) := nil;
992 Application.RemoveOnIdleHandler(AppOnIdle);
993 FNodes.Free;
994 inherited Destroy;
995 end;
996
997 procedure TBasicResizeFrame.FakeExitEnter(Sender: TObject);
998 begin
999 pL.Repaint;
1000 pT.Repaint;
1001 pR.Repaint;
1002 pB.Repaint;
1003 end;
1004
1005 procedure TBasicResizeFrame.FakeKeyDown(Sender: TObject; var Key: Word;
1006 Shift: TShiftState);
1007 var
1008 LWndProc: TWndMethod;
1009 LMsg: TLMKeyUp;
1010 begin
1011 LWndProc := FDesignedForm.Form.WindowProc;
1012 FillChar(LMsg{%H-}, SizeOf(LMsg), 0);
1013 LMsg.msg := CN_KEYDOWN;
1014 LMsg.CharCode := Key;
1015 LWndProc(TLMessage(LMsg));
1016 Key := LMsg.CharCode;
1017 end;
1018
1019 procedure TBasicResizeFrame.FakeKeyUp(Sender: TObject; var Key: Word;
1020 Shift: TShiftState);
1021 var
1022 LWndProc: TWndMethod;
1023 LMsg: TLMKeyUp;
1024 begin
1025 LWndProc := FDesignedForm.Form.WindowProc;
1026 FillChar(LMsg{%H-}, SizeOf(LMsg), 0);
1027 LMsg.msg := CN_KEYUP;
1028 LMsg.CharCode := Key;
1029 LWndProc(TLMessage(LMsg));
1030 Key := LMsg.CharCode;
1031 end;
1032
1033 procedure TBasicResizeFrame.FakeUTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char
1034 );
1035 begin
1036 FDesignedForm.Form.IntfUTF8KeyPress(UTF8Key, 1, False);
1037 end;
1038
1039 procedure TBasicResizeFrame.PositionNodes(AroundControl: TWinControl);
1040 var
1041 Node,T,L,CT,CL,FR,FB,FT,FL: Integer;
1042 TopLeft: TPoint;
1043 begin
1044 if FDesignedForm = nil then
1045 Exit;
1046
1047 // positions of bars
1048 if not FNodePositioning then
1049 begin
1050 pL.Left := -FHorizontalScrollPos;
1051 pR.Left := FDesignedForm.Width - FHorizontalScrollPos + pL.Width + BgRightMargin + BgLeftMargin;
1052 pT.Top := -FVerticalScrollPos;
1053 pB.Top := FDesignedForm.Height - FVerticalScrollPos + pT.Height + BgBottomMargin + BgTopMargin;
1054
1055 // width and height
1056 pL.Top:=0;
1057 pL.Height := FDesignedForm.Height + 2*SizerRectSize + BgTopMargin + BgBottomMargin;
1058 pR.Top:=0;
1059 pR.Height := FDesignedForm.Height + 2*SizerRectSize + BgTopMargin + BgBottomMargin;
1060 pT.Left:=0;
1061 pT.Width := FDesignedForm.Width + 2*SizerRectSize + BgLeftMargin + BgRightMargin;
1062 pB.Left:=0;
1063 pB.Width := FDesignedForm.Width + 2*SizerRectSize + BgLeftMargin + BgRightMargin;
1064
1065 // client
1066 if pBG.Left + BgLeftMargin <= 0 then
1067 pClient.Left := -(pBG.Left) - (FHorizontalScrollPos - SizerRectSize)
1068 else
1069 pClient.Left := pBG.Left + BgLeftMargin;
1070 if pBG.Top + BgTopMargin <= 0 then
1071 pClient.Top := -(pBG.Top) - (FVerticalScrollPos - SizerRectSize)
1072 else
1073 pClient.Top := pBG.Top + BgTopMargin;
1074
1075 pClient.Height := Height - pClient.Top - Max(Height - (pB.Top - BgBottomMargin), 0);
1076 pClient.Width := Width - pClient.Left - Max(Width - (pR.Left - BgRightMargin), 0);
1077 end;
1078
1079 AdjustFormHandler;
1080
1081 for Node := 0 to 7 do
1082 begin
1083 with AroundControl do
1084 begin
1085 FR := Width - RightSizerRectWidth - RightMargin;
1086 FB := Height - BottomSizerRectHeight - BottomMargin;
1087
1088 FT := TopSizerRectTop;
1089 FL := LeftSizerRectLeft;
1090
1091 CL := (FR - FL) div 2 + FL;
1092 CT := (FB - FT) div 2 + FT;
1093
1094 case Node of
1095 0: begin
1096 T := FT;
1097 L := FL;
1098 end;
1099 1: begin
1100 T := FT;
1101 L := CL;
1102 end;
1103 2: begin
1104 T := FT;
1105 L := FR;
1106 end;
1107 3: begin
1108 T := CT;
1109 L := FR;
1110 end;
1111 4: begin
1112 T := FB;
1113 L := FR;
1114 end;
1115 5: begin
1116 T := FB;
1117 L := CL;
1118 end;
1119 6: begin
1120 T := FB;
1121 L := FL;
1122 end;
1123 7: begin
1124 T := CT;
1125 L := FL;
1126 end;
1127 else
1128 T := 0;
1129 L := 0;
1130 end;
1131
1132 TopLeft := (Classes.Point(L,T));
1133 end;
1134 with TPanel(FNodes[Node]) do
1135 begin
1136 Top := TopLeft.Y;
1137 Left := TopLeft.X;
1138 Repaint;
1139 end;
1140 end;
1141 end;
1142
1143 end.
1144
1145