acctrls.pas

来自「FlexGraphics是一套创建矢量图形的VCL组件」· PAS 代码 · 共 404 行

PAS
404
字号
unit ACCtrls;

interface

uses
  Windows, Classes, Graphics, SysUtils, ExtCtrls,
  FlexBase, FlexProps, FlexUtils;

type
  TACControls = ( acNone, acZone, acDoor );

  TDoorState = ( dsClosed, dsOpened );
  TDoorOrientation = ( doVertical, doHorizontal );
  TDoorAccessDirection = ( ddUnknown, ddForward, ddBackward );
  TDoorEvent = ( deInactive, deValid, deError, deWarning, deBroken );

  TFlexDoor = class(TFlexControl)
  private
   FFrameIndex: integer;
   FLastFrameIndex: integer;
   FAnimTimer: TTimer;
   FPassAProp: TIntProp;
   FPassBProp: TIntProp;
   FOrientationProp: TEnumProp;
   FDoorStateProp: TEnumProp;
   FDoorEvent: TEnumProp;
   FAccessDir: TEnumProp;
   class procedure LoadResources;
   class procedure FreeResources;
   function  GetDoorOrientation: TDoorOrientation;
   function  GetDoorState: TDoorState;
   procedure SetDoorOrientation(Value: TDoorOrientation);
   procedure SetDoorState(Value: TDoorState);
   function  GetAccessDir: TDoorAccessDirection;
   function  GetDoorEvent: TDoorEvent;
   procedure SetAccessDir(Value: TDoorAccessDirection);
   procedure SetDoorEvent(Value: TDoorEvent);
  protected
   procedure CreateProperties; override;
   procedure ControlCreate; override;
   procedure ControlDestroy; override;
   procedure ControlTranslate(const TranslateInfo: TTranslateInfo); override;
   procedure Paint(Canvas: TCanvas; var PaintRect: TRect); override;
   procedure PropChanged(Sender: TObject; Prop: TCustomProp); override;
   procedure StartAnim;
   procedure StopAnim;
   procedure AnimTimer(Sender: TObject); virtual;
   function  CreateArrowBitmap(Orient: TDoorOrientation; StyleIndex: integer;
     IsForward: boolean): TBitmap; virtual;
   function  CreateFrameBitmap(Orient: TDoorOrientation;
     Index: integer): TBitmap; virtual;
   function  CreateImage: TBitmap;
   property  OrientationProp: TEnumProp read FOrientationProp;
   property  DoorStateProp: TEnumProp read FDoorStateProp;
   property  DoorEventProp: TEnumProp read FDoorEvent;
  public
   class function GetToolInfo(ToolIcon: TBitmap; var Hint: string): boolean;
     override;
   function  IsPointInside(PaintX, PaintY: integer): boolean; override;
   function  GetAnyExistPass: integer;
   property  PassAProp: TIntProp read FPassAProp;
   property  PassBProp: TIntProp read FPassBProp;
   property  Orientation: TDoorOrientation read GetDoorOrientation
     write SetDoorOrientation;
   property  DoorState: TDoorState read GetDoorState write SetDoorState;
   property  AccessDir: TDoorAccessDirection read GetAccessDir
     write SetAccessDir;
   property  DoorEvent: TDoorEvent read GetDoorEvent write SetDoorEvent;
  end;

procedure FindDoorsForPass(PassID: integer; List: TList);
function  GetACType(Control: TFlexControl): TACControls;

implementation

{$R Door.res Door.rc}

var
  FDoorImages: TBitmap;
  FDoorArrows: TBitmap;
  FDoorIcon: TBitmap;

  FDoorList: TList;

procedure FindDoorsForPass(PassID: integer; List: TList);
var i: integer;
begin
 List.Clear;
 for i:=0 to FDoorList.Count-1 do with TFlexDoor(FDoorList[i]) do
  if (PassID < 0) or
     (PassAProp.Value = PassID) or (PassBProp.Value = PassID) then
   List.Add(FDoorList[i]);
end;

function GetACType(Control: TFlexControl): TACControls;
var sType: string;
begin
 Result := acNone;
 if not Assigned(Control) then exit;
 sType := Control.UserData.Values['Type'];
 if sType = 'Door' then Result := acDoor else
 if sType = 'Zone' then Result := acZone;
end;

// TFlexDoor //////////////////////////////////////////////////////////////////

procedure TFlexDoor.ControlCreate;
begin
 UserData.Values['Type'] := 'Door';
 if Assigned(FDoorImages) then begin
  Width := ScalePixels(FDoorImages.Height div 2);
  Height := Width;
  FLastFrameIndex := FDoorImages.Width div UnScalePixels(Width) -1;
 end else begin
  Width := ScalePixels(20);
  Height := ScalePixels(20);
  FLastFrameIndex := 0;
 end;
 WidthProp.Style := WidthProp.Style + [psReadOnly];
 HeightProp.Style := HeightProp.Style + [psReadOnly];
 FFrameIndex := 0;
 if not Assigned(FDoorList) then FDoorList := TList.Create;
 FDoorList.Add(Self);
 inherited;
 Visible := True;
end;

procedure TFlexDoor.CreateProperties;
begin
 inherited;
 FPassAProp := TIntProp.Create(Props, 'PassA');
 FPassBProp := TIntProp.Create(Props, 'PassB');
 FOrientationProp := TEnumProp.Create(Props, 'Orientation');
 FOrientationProp.AddItem('Vertical');
 FOrientationProp.AddItem('Horizontal');
 FDoorStateProp := TEnumProp.Create(Props, 'DoorState');
 FDoorStateProp.AddItem('Closed');
 FDoorStateProp.AddItem('Opened');
 FAccessDir := TEnumProp.Create(Props, 'AccessDir');
 FAccessDir.AddItem('Unknown');
 FAccessDir.AddItem('Forward');
 FAccessDir.AddItem('Backward');
 FDoorEvent := TEnumProp.Create(Props, 'DoorEvent');
 FDoorEvent.AddItem('Inactive');
 FDoorEvent.AddItem('Valid');
 FDoorEvent.AddItem('Error');
 FDoorEvent.AddItem('Warning');
 FDoorEvent.AddItem('Broken');
end;

procedure TFlexDoor.ControlTranslate(const TranslateInfo: TTranslateInfo);
var Degree: integer;
begin
 inherited;
 Degree := TranslateInfo.Rotate mod 360 div 90;
 if Degree < 0 then Degree := 4 + Degree;
 if Degree and 1 <> 0 then
  FOrientationProp.EnumIndex := 1 - FOrientationProp.EnumIndex;  
end;

procedure TFlexDoor.ControlDestroy;
begin
 StopAnim;
 FDoorList.Remove(Self);
 if FDoorList.Count = 0 then FreeAndNil(FDoorList);
 inherited;
end;

class procedure TFlexDoor.LoadResources;
begin
 if not Assigned(FDoorImages) then FDoorImages := TBitmap.Create;
 FDoorImages.LoadFromResourceName(HInstance, 'DOORIMAGES');
 if not Assigned(FDoorArrows) then FDoorArrows := TBitmap.Create;
 FDoorArrows.LoadFromResourceName(HInstance, 'DOORARROWS');
 if not Assigned(FDoorIcon) then FDoorIcon := TBitmap.Create;
 FDoorIcon.LoadFromResourceName(HInstance, 'DOORICON');
end;

class procedure TFlexDoor.FreeResources;
begin
 FreeAndNil(FDoorImages);
 FreeAndNil(FDoorArrows);
 FreeAndNil(FDoorIcon);
end;

class function TFlexDoor.GetToolInfo(ToolIcon: TBitmap;
  var Hint: string): boolean;
begin
 Result := true;
 Hint := 'AC Door tool';
 if Assigned(ToolIcon) and Assigned(FDoorIcon) then
  ToolIcon.Canvas.Draw(0, 0, FDoorIcon);
end;

function TFlexDoor.GetDoorOrientation: TDoorOrientation;
begin
 Result := TDoorOrientation(FOrientationProp.EnumIndex);
end;

function TFlexDoor.GetDoorState: TDoorState;
begin
 Result := TDoorState(FDoorStateProp.EnumIndex);
end;

procedure TFlexDoor.SetDoorOrientation(Value: TDoorOrientation);
begin
 FOrientationProp.EnumIndex := integer(Value);
end;

procedure TFlexDoor.SetDoorState(Value: TDoorState);
begin
 FDoorStateProp.EnumIndex := integer(Value);
end;

function TFlexDoor.GetAccessDir: TDoorAccessDirection;
begin
 Result := TDoorAccessDirection(FAccessDir.EnumIndex);
end;

function TFlexDoor.GetDoorEvent: TDoorEvent;
begin
 Result := TDoorEvent(FDoorEvent.EnumIndex);
end;

procedure TFlexDoor.SetAccessDir(Value: TDoorAccessDirection);
begin
 FAccessDir.EnumIndex := integer(Value);
end;

procedure TFlexDoor.SetDoorEvent(Value: TDoorEvent);
begin
 FDoorEvent.EnumIndex := integer(Value);
end;

function TFlexDoor.GetAnyExistPass: integer;
begin
 if PassAProp.Value > 0 then
  Result := PassAProp.Value
 else
 if PassBProp.Value > 0 then
  Result := PassBProp.Value
 else
  Result := 0;
end;

function TFlexDoor.CreateArrowBitmap(Orient: TDoorOrientation;
  StyleIndex: integer; IsForward: boolean): TBitmap;
var Img: TPoint;
    Size: integer;
begin
 if not Assigned(FDoorArrows) then begin
  Result := Nil;
  exit;
 end;
 Img.Y := StyleIndex;
 if Orient = doHorizontal then begin
  if IsForward
   then Img.X := 2
   else Img.X := 1;
 end else
  if IsForward
   then Img.X := 3
   else Img.X := 0;
 Size := (FDoorArrows.Width div 4);
 Img.X := Img.X * Size;
 Img.Y := Img.Y * Size;
 Result := TBitmap.Create;
 Result.Width := Size;
 Result.Height := Size;
 Result.Canvas.CopyRect(Rect(0, 0, Size, Size), FDoorArrows.Canvas,
   Rect(Img.X, Img.Y, Img.X + Size, Img.Y + Size));
 Result.Transparent := True;
end;

function TFlexDoor.CreateFrameBitmap(Orient: TDoorOrientation;
  Index: integer): TBitmap;
var Img: TPoint;
    PicSize: TPoint;
begin
 if not Assigned(FDoorImages) then begin
  Result := Nil;
  exit;
 end;
 PicSize.X := UnScalePixels(Width);
 PicSize.Y := UnScalePixels(Height);
 Img.X := Index * PicSize.X;
 if Orient = doVertical
  then Img.Y := 0
  else Img.Y := PicSize.Y;
 Result := TBitmap.Create;
 Result.Width := PicSize.X;
 Result.Height := PicSize.Y;
 Result.Canvas.CopyRect(Rect(0, 0, PicSize.X, PicSize.Y), FDoorImages.Canvas,
   Rect(Img.X, Img.Y, Img.X + PicSize.X, Img.Y + PicSize.Y));
 Result.Transparent := True;
end;

function TFlexDoor.CreateImage: TBitmap;
var Arrow: TBitmap;
    Img: TPoint;
begin
 Result := CreateFrameBitmap(Orientation, FFrameIndex);
 if not Assigned(Result) then exit;
 if ((DoorState = dsOpened) or Assigned(FAnimTimer)) and
    (AccessDir <> ddUnknown) or (DoorEvent = deBroken) then begin
  Arrow := CreateArrowBitmap(Orientation, integer(DoorEvent),
    AccessDir = ddForward);
  Img.X := (UnScalePixels(Width) - Arrow.Width) div 2;
  Img.Y := (UnScalePixels(Height) - Arrow.Height) div 2;
  if Assigned(Arrow) then Result.Canvas.Draw(Img.X, Img.Y, Arrow);
  Arrow.Free;
 end;
end;

procedure TFlexDoor.Paint(Canvas: TCanvas; var PaintRect: TRect);
var Frame: TBitmap;
begin
 if not Assigned(FDoorImages) then exit;
 Frame := CreateImage;
 try
  Canvas.StretchDraw(PaintRect, Frame);
 finally
  Frame.Free;
 end;
end;

function TFlexDoor.IsPointInside(PaintX, PaintY: integer): boolean;
var Frame: TBitmap;
    P: TPoint;
begin
 Result := inherited IsPointInside(PaintX, PaintY);
 if not Result then exit;
 P := OwnerToClient(Point(PaintX, PaintY));
 Frame := CreateFrameBitmap(Orientation, 0);
 try
  if Frame.Canvas.Pixels[P.X, P.Y] and $FFFFFF =
   Frame.TransparentColor and $FFFFFF then Result := False;
 finally
  Frame.Free;
 end;
end;

procedure TFlexDoor.StartAnim;
begin
 if not Assigned(FAnimTimer) then begin
  FAnimTimer := TTimer.Create(Nil);
  FAnimTimer.Interval := 20;
  FAnimTimer.OnTimer := AnimTimer;
 end;
 AnimTimer(FAnimTimer);
end;

procedure TFlexDoor.StopAnim;
begin
 FreeAndNil(FAnimTimer);
 Invalidate;
end;

procedure TFlexDoor.AnimTimer(Sender: TObject);
begin
 FAnimTimer.Enabled := False;
 try
  if DoorState = dsClosed then begin
   if FFrameIndex > 0 then begin
    dec(FFrameIndex);
    Invalidate;
   end else
    StopAnim;
  end else
  if DoorState = dsOpened then begin
   if FFrameIndex < FLastFrameIndex then begin
    inc(FFrameIndex);
    Invalidate;
   end else
    StopAnim;
  end else
   StopAnim;
 finally
  if Assigned(FAnimTimer) then FAnimTimer.Enabled := True;
 end;
end;

procedure TFlexDoor.PropChanged(Sender: TObject; Prop: TCustomProp);
begin
 inherited;
 if Prop = FDoorStateProp then StartAnim;
end;

///////////////////////////////////////////////////////////////////////////////

procedure RegisterACControls;
begin
 RegisterFlexControl(TFlexDoor);
end;

initialization
  TFlexDoor.LoadResources;
  RegisterACControls;

finalization
  TFlexDoor.FreeResources;

end.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?