GUIFitter.pas
上传用户:yj_qiu
上传日期:2022-08-08
资源大小:23636k
文件大小:10k
源码类别:

游戏引擎

开发平台:

Delphi

  1. (*
  2.  @Abstract(GUI fitter unit)
  3.  (C) 2006 George "Mirage" Bakhtadze. <a href="http://www.casteng.com">www.casteng.com</a> <br>
  4.  The source code may be used under either MPL 1.1 or LGPL 2.1 license. See included license.txt file <br>
  5.  Unit contains base GUI fitter class and 2D fitter class
  6. *)
  7. {$Include GDefines.inc}
  8. unit GUIFitter;
  9. interface
  10. uses
  11.   TextFile, SysUtils,
  12.   BaseTypes, Basics, Props, Models, BaseClasses, BaseMsg, BaseGraph, ACSBase,
  13.   Base3D;
  14. const
  15.   // Hot areas
  16.   haCenter = 0; haTop = 1; haLeft = 2; haRight = 3; haBottom = 4;
  17.   haTopLeft = 5; haTopRight = 6; haBottomLeft = 7; haBottomRight = 8;
  18.     // Aliases
  19.   haXMove = 1; haYMove = 2; haZMove = 3;
  20.   haXRotate = 4; haYRotate = 5; haZRotate = 6;
  21. type
  22.   TFitterOpParams = record
  23.     X, Y, Width, Height: Single;
  24.   end;
  25.   TGUIFitterOp = class(Models.TOperation)
  26.   private
  27.     AffectedGUIItem: TGUIItem;
  28.     Params: TFitterOpParams;
  29.   protected
  30.     procedure DoApply; override;
  31.     function DoMerge(AOperation: Models.TOperation): Boolean; override;
  32.   public
  33.     function Init(AAffectedGUIItem: TGUIItem; AX, AY, AWidth, AHeight: Single): Boolean;
  34.   end;
  35.   TFitter = class(TGUIItem)
  36.   protected
  37.     HoverArea: Integer;
  38.     PushX, PushY: Single;
  39.     Areas: array of BaseTypes.TArea;
  40.     RoundShift: Single;                                              // A value used to draw "rounded" hot spots
  41.     procedure BuildAreas; virtual; abstract;
  42.     procedure HandleMove(AX, AY: Single); virtual; abstract;
  43.     function GetAffectedItem: TItem; virtual; abstract;
  44.     procedure SetAffectedItem(const Value: TItem); virtual; abstract;
  45.   public
  46.     UseOperations: Boolean;
  47.     DefaultSize: Single;
  48.     constructor Create(AManager: TItemsManager); override;
  49.     function IsWithin(AX, AY: Single): Boolean; override;
  50.     procedure ResetFitter; virtual;
  51.     property AffectedItem: TItem read GetAffectedItem write SetAffectedItem;
  52.   end;
  53.   T2DFitter = class(TFitter)
  54.   private
  55.     XSize, YSize: Single;
  56.   protected
  57.     procedure BuildAreas; override;
  58.     procedure HandleMove(AX, AY: Single); override;
  59.     function GetAffectedItem: TItem; override;
  60.     procedure SetAffectedItem(const Value: TItem); override;
  61.   public
  62.     AffectedGUIItem: TGUIItem;
  63.     GridX, GridY: Integer;
  64.     constructor Create(AManager: TItemsManager); override;
  65.     destructor Destroy; override;
  66.     function GUIHandleMessage(const Msg: TMessage): Boolean; override;
  67.     procedure Draw; override;
  68.   end;
  69. implementation
  70. { TFitter }
  71. constructor TFitter.Create(AManager: TItemsManager);
  72. var i: Integer;
  73. begin
  74.   inherited;
  75.   DefaultSize := 8.0;
  76.   SetLength(Areas, 9);
  77.   for i := 0 to High(Areas) do Areas[i] := GetArea(-1, -1, -1, -1);
  78.   HoverArea := -1;
  79.   RoundShift := 2;
  80. end;
  81. function TFitter.IsWithin(AX, AY: Single): Boolean;
  82. var i: Integer;
  83. begin
  84.   ScreenToClient(AX, AY);
  85.   i := High(Areas);
  86.   while (i >= 0) and not IsInArea(AX, AY, Areas[i]) do Dec(i);
  87.   Result := i >= 0; 
  88. end;
  89. procedure TFitter.ResetFitter;
  90. // Resets fitter's state. Used if the fitter is unable to receive all messages (e.g. TGUIMouseUp)
  91. begin
  92.   HoverArea := -1;
  93.   FFocused  := False;
  94.   Hover     := False;
  95.   Pushed    := False;
  96. end;
  97. { T2DFitter }
  98. procedure T2DFitter.BuildAreas;
  99. begin
  100.   XSize := MaxS(2, MinS(DefaultSize, Width/4));
  101.   YSize := MaxS(2, MinS(DefaultSize, Height/4));
  102.   Areas[haCenter]      := GetArea((Width-XSize)*0.5, (Height-YSize)*0.5, (Width+XSize)*0.5, (Height+YSize)*0.5);
  103.   Areas[haTop]         := GetArea((Width-XSize)*0.5, 0,                  (Width+XSize)*0.5, YSize);
  104.   Areas[haLeft]        := GetArea(0,                 (Height-YSize)*0.5, XSize,             (Height+YSize)*0.5);
  105.   Areas[haRight]       := GetArea(Width-XSize,       (Height-YSize)*0.5, Width,             (Height+YSize)*0.5);
  106.   Areas[haBottom]      := GetArea((Width-XSize)*0.5, Height-YSize,       (Width+XSize)*0.5, Height);
  107.   Areas[haTopLeft]     := GetArea(0,                 0,                  XSize,             YSize);
  108.   Areas[haTopRight]    := GetArea(Width-XSize,       0,                  Width,             YSize);
  109.   Areas[haBottomLeft]  := GetArea(0,                 Height-YSize,       XSize,             Height);
  110.   Areas[haBottomRight] := GetArea(Width-XSize,       Height-YSize,       Width,             Height);
  111. end;
  112. procedure T2DFitter.HandleMove(AX, AY: Single);
  113. var t: Single; Op: TGUIFitterOp;
  114. begin
  115. //  OldX := X;
  116. //  OldY := Y;
  117.   AX := MaxS(-MaxInt, MinS(MaxInt, AX));
  118.   AY := MaxS(-MaxInt, MinS(MaxInt, AY));
  119.   AX := Frac(AX) + Trunc(AX) div GridX * GridX;
  120.   AY := Frac(AY) + Trunc(AY) div GridY * GridY;
  121.   if (AX = PushX) and (AY = PushY) then Exit;
  122.   if HoverArea = haCenter then begin                                        // Center hot area
  123.     X := X + (AX - PushX);
  124.     Y := Y + (AY - PushY);
  125.   end;
  126.   if HoverArea in [haTop, haTopLeft, haTopRight] then begin                 // Top hot areas
  127.     t := Height;
  128.     Height := Height - (AY - PushY);
  129.     Height := Frac(Height) + Trunc(Height) div GridY * GridY - Trunc(Y) mod GridY;
  130.     Y := Y + (t - Height);
  131.   end;
  132.   if HoverArea in [haLeft, haTopLeft, haBottomLeft] then begin              // Left hot areas
  133.     t := Width;
  134.     Width := Width - (AX - PushX);
  135.     Width := Frac(Width) + Trunc(Width) div GridX * GridX - Trunc(X) mod GridX;
  136.     X := X + (t - Width);
  137.   end;
  138.   if HoverArea in [haRight, haTopRight, haBottomRight] then begin           // Right hot areas
  139.     Width := Width + (AX - PushX);
  140.     Width := Frac(Width) + Trunc(Width) div GridX * GridX - Trunc(X) mod GridX;
  141.     PushX := Width - GridX;// - Trunc(X) mod GridX;
  142.     //AX - Trunc(X) mod GridX;
  143.   end;
  144.   if HoverArea in [haBottom, haBottomLeft, haBottomRight] then begin        // Bottom hot areas
  145.     Height := Height + (AY - PushY);
  146.     Height := Frac(Height) + Trunc(Height) div GridY * GridY - Trunc(Y) mod GridY;
  147.     PushY := Height - GridY;
  148.   end;
  149.   if HoverArea in [haCenter, haLeft, haTopLeft, haBottomLeft] then          // Snap X to grid
  150.     X := Frac(X) + Trunc(X) div GridX * GridX;
  151.   if HoverArea in [haCenter, haTop, haTopLeft, haTopRight] then             // Snap Y to grid
  152.     Y := Frac(Y) + Trunc(Y) div GridY * GridY;
  153.   if Assigned(AffectedItem) then
  154.     if UseOperations then begin
  155.       Op := TGUIFitterOp.Create;
  156.       if Op.Init(AffectedGUIItem, X, Y, Width, Height) then
  157.         SendMessage(TOperationMsg.Create(Op), nil, [mfCore])
  158.       else
  159.         Op.Free;
  160.     end else begin
  161.       AffectedGUIItem.PxWidth  := Width;
  162.       AffectedGUIItem.PxHeight := Height;
  163.       AffectedGUIItem.PxX      := X;//AffectedGUIItem.PxX + (X - OldX);
  164.       AffectedGUIItem.PxY      := Y;//AffectedGUIItem.PxY + (Y - OldY);
  165.     end;
  166. end;
  167. function T2DFitter.GetAffectedItem: TItem;
  168. begin
  169.   Result := AffectedGUIItem;
  170. end;
  171. procedure T2DFitter.SetAffectedItem(const Value: TItem);
  172. begin
  173.   if Value is TGUIItem then begin
  174.     AffectedGUIItem := Value as TGUIItem;
  175.     Parent := AffectedGUIItem.Parent;
  176. {    Parent := nil;
  177.     AffectedGUIItem.Parent.InsertChild(Self, 0);}
  178.   end else begin
  179.     {$IFDEF LOGGING} Log.Log(ClassName + '.SetAffectedItem: Affected item is not a TGUIItem', lkError); {$ENDIF}
  180.   end;
  181. end;
  182. constructor T2DFitter.Create(AManager: TItemsManager);
  183. begin
  184.   inherited;
  185.   GridX := 10;
  186.   GridY := 10;
  187.   Align := alAbsolute;
  188. end;
  189. destructor T2DFitter.Destroy;
  190. begin
  191.   Areas := nil;
  192.   inherited;
  193. end;
  194. procedure T2DFitter.Draw;
  195. var i: Integer;
  196. begin
  197.   inherited;
  198.   BuildAreas;
  199.   Screen.SetColor(NormalColor);
  200.   for i := haCenter to haBottomRight do if i <> HoverArea then
  201.     Screen.Bar(Areas[i].X1, Areas[i].Y1, Areas[i].X2, Areas[i].Y2);
  202.   Screen.SetColor(Color);
  203.   if HoverArea <> -1 then
  204.     Screen.Bar(Areas[HoverArea].X1, Areas[HoverArea].Y1, Areas[HoverArea].X2, Areas[HoverArea].Y2);
  205. end;
  206. function T2DFitter.GUIHandleMessage(const Msg: TMessage): Boolean;
  207. var OMX, OMY: Integer; MX, MY: Single; Processed: Boolean;
  208. begin
  209.   if Msg is TMouseMsg then with TMouseMsg(Msg) do begin
  210.     OMX := X; OMY := Y;
  211.     MX := X; MY := Y;
  212.     ScreenToClient(MX, MY);
  213.   end else begin
  214.     OMX := 0; OMY := 0;
  215.   end;  
  216.   Result := inherited GUIHandleMessage(Msg);
  217.   if not Result then Exit;
  218.   if Msg is TMouseMsg then with TMouseMsg(Msg) do begin
  219.     Processed := False;
  220.     if (Msg.ClassType = TMouseDownMsg) then begin
  221.       if Hover then begin
  222.         PushX := Frac(MX) + Trunc(MX) div GridX * GridX;
  223.         PushY := Frac(MY) + Trunc(MY) div GridY * GridY;
  224.         Processed := True;
  225.       end;
  226.     end else if (Msg.ClassType = TMouseMoveMsg) then begin
  227.       Processed := True;
  228.       if Pushed then HandleMove(MX, MY) else begin
  229.         HoverArea := haCenter;
  230.         while (HoverArea <= haBottomRight) and not IsInArea(MX, MY, Areas[HoverArea]) do Inc(HoverArea);
  231.         Processed := HoverArea <= haBottomRight;
  232.         if not Processed then HoverArea := -1;
  233.       end;
  234.     end;
  235.     if not Processed then begin                           // Restore mouse coordinates to allow the message handling by other controls
  236.       X := OMX;
  237.       Y := OMY;
  238.     end;
  239.   end;
  240. end;
  241. { TGUIFitterOp }
  242. procedure TGUIFitterOp.DoApply;
  243. var t: Single;
  244. begin
  245.   t                        := AffectedGUIItem.PxWidth;
  246.   AffectedGUIItem.PxWidth  := Params.Width;
  247.   Params.Width             := t;
  248.   t                        := AffectedGUIItem.PxHeight;
  249.   AffectedGUIItem.PxHeight := Params.Height;
  250.   Params.Height            := t;
  251.   t                        := AffectedGUIItem.PxX;
  252.   AffectedGUIItem.PxX      := Params.X;
  253.   Params.X                 := t;
  254.   t                        := AffectedGUIItem.PxY;
  255.   AffectedGUIItem.PxY      := Params.Y;
  256.   Params.Y                 := t;
  257. end;
  258. function TGUIFitterOp.DoMerge(AOperation: TOperation): Boolean;
  259. begin
  260.   Result := (AOperation is TGUIFitterOp) and (TGUIFitterOp(AOperation).AffectedGUIItem = AffectedGUIItem);
  261.   if Result and not (ofApplied in Flags) then Params := TGUIFitterOp(AOperation).Params;
  262. end;
  263. function TGUIFitterOp.Init(AAffectedGUIItem: TGUIItem; AX, AY, AWidth, AHeight: Single): Boolean;
  264. begin
  265.   Assert(Assigned(AAffectedGUIItem));
  266.   Result := False;
  267.   if (AAffectedGUIItem.X = AX) and (AAffectedGUIItem.Y = AY) and (AAffectedGUIItem.Width = AWidth) and (AAffectedGUIItem.Height = AHeight) then Exit;
  268.   AffectedGUIItem := AAffectedGUIItem;
  269.   Params.X        := AX;
  270.   Params.Y        := AY;
  271.   Params.Width    := AWidth;
  272.   Params.Height   := AHeight;
  273.   Result := True;
  274. end;
  275. end.