《GOF设计模式》—备忘录(MEMENTO)—Delphi源码示例:图形编辑器

    技术2022-05-19  28

    示例:图形编辑器 说明: 考虑一个图形编辑器,它支持图形对象间的连线。用户可用一条直线连接两个矩形,而当用户移动任意一个矩形时,这两个矩形仍能保持连接。在移动过程中,编辑器自动伸展这条直线以保持该连接。 我们可用备忘录(Memento)模式实现移动操作取消。 界面:   object Form1: TForm1   Left = 192   Top = 113   Width = 400   Height = 270   Caption = 'Form1'   Color = clBtnFace   Font.Charset = DEFAULT_CHARSET   Font.Color = clWindowText   Font.Height = -11   Font.Name = 'MS Sans Serif'   Font.Style = []   OldCreateOrder = False   OnCreate = FormCreate   OnDestroy = FormDestroy   OnMouseDown = FormMouseDown   OnMouseUp = FormMouseUp   PixelsPerInch = 96   TextHeight = 13   object Button1: TButton     Left = 40     Top = 184     Width = 75     Height = 25     Caption = '创建图形'     TabOrder = 0     OnClick = Button1Click   end   object Button2: TButton     Left = 144     Top = 184     Width = 75     Height = 25     Caption = '取消移动'     TabOrder = 1     OnClick = Button2Click   end end 代码:   unit uGraphic; interface uses     Windows,SysUtils,Classes,Graphics,Contnrs; type     TGraphic = class;     {约束信息}     TConstraintInfo = record         StartConnection,EndConnection: TGraphic;         StartPosition,EndPosition: TPoint;     end;     PConstraintInfo = ^TConstraintInfo;     TConstraints = class(TList)     private         function GetItems(Index: integer): PConstraintInfo;     protected         procedure Notify(Ptr: Pointer; Action: TListNotification); override;     public         procedure Add(const AStartConnection,AEndConnection: TGraphic); overload;         function IndexOf(const AStartConnection,AEndConnection: TGraphic): Integer; overload;         procedure Assign(const AConstraints: TConstraints);         //---         property Items[Index: integer]: PConstraintInfo read GetItems;     end;     TGraphic = class     private         FCanvas: TCanvas;         FPosition: TPoint;         procedure Clear;     public         constructor Create(ACanvas: TCanvas; APosition: TPoint);         //---         function GetRect: TRect;         procedure Draw;         procedure Move(p: TPoint);         //---         property Position: TPoint read FPosition;     end;     TGraphics = class(TObjectList)     private         function GetItems(Index: Integer): TGraphic;     public         function GetGraphic(const APosition: TPoint): TGraphic;         //---         property Items[Index: Integer]: TGraphic read GetItems;     end;     TMemento = class     end;     TConstraintSolverMemento = class(TMemento)     private         FConstraints: TConstraints;     public         constructor Create(AConstraints: TConstraints);         destructor Destroy; override;     end;     {约束解释器,采用单件模式}     TConstraintSolver = class     private         FCanvas: TCanvas;         FConstraints: TConstraints;         FIsDraw: boolean;         procedure DrawLine(pInfo: PConstraintInfo);         procedure ClearLines;     public         constructor Create;         destructor Destroy; override;         //---         class function Instance: TConstraintSolver;         //---         procedure Solve();         //---         procedure AddConstraint(const AStartConnection,AEndConnection: TGraphic);         procedure RemoveConctraint(const AStartConnection,AEndConnection: TGraphic);         //---         function CreateMemento(): TMemento;         procedure SetMemento(m: TMemento);         //---         property Canvas: TCanvas write FCanvas;     end;     {命令,采用命令模式}     TCommand = class     public         procedure Execute(); virtual; abstract;         procedure Unexecute(); virtual; abstract;     end;     TMoveCommand = class(TCommand)     private         FTarget: TGraphic;         FDelta: TPoint;         FState: TMemento;     public         constructor Create(ATarget: TGraphic; ADelta: TPoint);         destructor Destroy; override;         //---         procedure Execute(); override;         procedure Unexecute(); override;     end;     TSelectCommand = class(TCommand)     private         FGraphics: TGraphics;         FCurGraphic: TGraphic;         FPosition: TPoint;     public         constructor Create(AGraphics: TGraphics);         //---         procedure Execute; override;         procedure Unexecute; override;         //---         property Position: TPoint write FPosition;         property CurGraphic: TGraphic read FCurGraphic write FCurGraphic;     end;     TGraphicManipulator = class     private         FSelectCommand: TSelectCommand;         FMoveCommand: TMoveCommand;         procedure ClearMoveCommand;     public         constructor Create(AGraphics: TGraphics);         destructor Destroy; override;         //---         procedure MouseDown(X,Y: Integer);         procedure MouseMove(X,Y: Integer);         procedure MouseUp(X,Y: Integer);         //---         property MoveCommand: TMoveCommand read FMoveCommand;     end; implementation var     FConstraintSolver: TConstraintSolver; procedure TGraphic.Draw; begin     with FCanvas do     begin         with Pen do         begin             Color := clYellow;             Style := psSolid;             Width := 1;             Mode := pmXor;         end;         //---         Rectangle(self.GetRect);     end; end; procedure TGraphic.Clear; begin     Draw; end; constructor TGraphic.Create(ACanvas: TCanvas; APosition: TPoint); begin     FCanvas := ACanvas;     FPosition := APosition; end; procedure TGraphic.Move(p: TPoint); begin     if (FPosition.X <> 0) or (FPosition.Y <> 0) then         Clear;     //---     with FPosition do     begin         X := X + p.X;         Y := Y + p.Y;     end;     //---     Draw; end; constructor TMoveCommand.Create(ATarget: TGraphic; ADelta: TPoint); begin     inherited Create;     //---     FTarget := ATarget;     FDelta := ADelta;     FState := nil; end; destructor TMoveCommand.Destroy; begin     if FState <> nil then         FState.Free;     //---     inherited; end; procedure TMoveCommand.Execute(); var     ASolver: TConstraintSolver; begin     ASolver := TConstraintSolver.Instance;     //---     if FState <> nil then         FState.Free;     FState := ASolver.CreateMemento();     //---     FTarget.Move(FDelta);     //---     ASolver.Solve; end; procedure TMoveCommand.Unexecute(); var     ASolver: TConstraintSolver; begin     ASolver := TConstraintSolver.Instance;     //---     FDelta.x := -FDelta.x;     FDelta.y := -FDelta.y;     FTarget.Move(FDelta);     //---     ASolver.SetMemento(FState);     ASolver.Solve; end; constructor TConstraintSolverMemento.Create(AConstraints: TConstraints); begin     inherited Create;     //---     FConstraints := TConstraints.Create;     FConstraints.Assign(AConstraints); end; destructor TConstraintSolverMemento.Destroy; begin     FConstraints.Free;     //---     inherited; end; constructor TConstraintSolver.Create; begin     if FConstraintSolver = nil then     begin         FConstraintSolver := Self;         FConstraints := TConstraints.Create;     end     else         abort; end; destructor TConstraintSolver.Destroy; begin     FConstraintSolver := nil;     FConstraints.Free;     //---     inherited; end; procedure TConstraintSolver.Solve();     //---     procedure _RefreshPositions;     var         i: Integer;     begin         with FConstraints do         begin             for i := 0 to Count - 1 do             begin                 with Items[i]^ do                 begin                     StartPosition := StartConnection.Position;                     EndPosition := EndConnection.Position;                 end;             end;         end;     end;     //---     procedure _DrawLines;     var         i: Integer;     begin         with FConstraints do         begin             for i := 0 to Count - 1 do                 DrawLine(Items[i]);         end;         //---         FIsDraw := True;     end; begin     ClearLines;     _RefreshPositions;     _DrawLines; end; procedure TConstraintSolver.AddConstraint(const AStartConnection,     AEndConnection: TGraphic); begin     FConstraints.Add(AStartConnection,AEndConnection); end; procedure TConstraintSolver.ClearLines;     //---     procedure _ClearLine(pInfo: PConstraintInfo);     begin         DrawLine(pInfo);     end; var     i: Integer; begin     if FIsDraw then     begin         with FConstraints do         begin             for i := 0 to Count - 1 do                 _ClearLine(Items[i]);         end;         //---         FIsDraw := false;     end; end; procedure TConstraintSolver.RemoveConctraint(const AStartConnection,     AEndConnection: TGraphic); var     AIndex: Integer; begin     AIndex := FConstraints.IndexOf(AStartConnection,AEndConnection);     if AIndex >= 0 then         FConstraints.Delete(AIndex); end; function TConstraintSolver.CreateMemento(): TMemento; begin     Result := TConstraintSolverMemento.Create(FConstraints); end; procedure TConstraintSolver.DrawLine(pInfo: PConstraintInfo); begin     with FCanvas do     begin         with Pen do         begin             Color := clYellow;             Style := psSolid;             Width := 1;             Mode := pmXor;         end;         //---         with pInfo^ do         begin             MoveTo(StartPosition.X,StartPosition.Y);             LineTo(EndPosition.X,EndPosition.Y);         end;     end; end; procedure TConstraintSolver.SetMemento(m: TMemento); begin     if m is TConstraintSolverMemento then     begin         ClearLines;         self.FConstraints.Assign(TConstraintSolverMemento(m).FConstraints);     end; end; class function TConstraintSolver.Instance: TConstraintSolver; begin     if FConstraintSolver = nil then         FConstraintSolver := TConstraintSolver.Create;     //---     Result := FConstraintSolver; end; function TConstraints.GetItems(Index: integer): PConstraintInfo; begin     Result := Get(Index); end; function TConstraints.IndexOf(const AStartConnection,AEndConnection: TGraphic):     Integer; var     i: Integer; begin     for i := 0 to Count - 1 do     begin         with Items[i]^ do         begin             if (StartConnection = AStartConnection) and (EndConnection = AEndConnection) then             begin                 Result := i;                 Exit;             end;         end;     end;     //---     Result := -1; end; procedure TConstraints.Assign(const AConstraints: TConstraints); var     i: Integer;     pInfo: PConstraintInfo; begin     Self.Clear;     //---     with AConstraints do     begin         for i := 0 to Count - 1 do         begin             New(pInfo);             pInfo^ := Items[i]^;             self.Add(pInfo);         end;     end; end; procedure TConstraints.Notify(Ptr: Pointer; Action: TListNotification); begin     if Action = lnDeleted then         Dispose(Ptr); end; function TGraphic.GetRect: TRect; begin     with FPosition do         Result := Rect(X - 10,Y - 10,X + 10,Y + 10); end; function TGraphics.GetGraphic(const APosition: TPoint): TGraphic; var     i: integer; begin     for i := 0 to self.Count - 1 do     begin         if PtInRect(self.Items[i].GetRect,APosition) then         begin             Result := self.Items[i];             Exit;         end;     end;     //---     Result := nil; end; function TGraphics.GetItems(Index: Integer): TGraphic; begin     Result := TGraphic(inherited Items[Index]); end; procedure TConstraints.Add(const AStartConnection,     AEndConnection: TGraphic); var     pInfo: PConstraintInfo; begin     New(pInfo);     with pInfo^ do     begin         StartConnection := AStartConnection;         EndConnection := AEndConnection;         StartPosition := Point(0,0);         EndPosition := Point(0,0);     end;     //---     self.Add(pInfo); end; constructor TSelectCommand.Create(AGraphics: TGraphics); begin     inherited Create;     //---     FGraphics := AGraphics; end; procedure TSelectCommand.Execute; begin     FCurGraphic := FGraphics.GetGraphic(FPosition) end; procedure TSelectCommand.Unexecute; begin end; { TGraphicManipulator } procedure TGraphicManipulator.ClearMoveCommand; begin     if FMoveCommand <> nil then         FMoveCommand.Free;     FMoveCommand := nil; end; constructor TGraphicManipulator.Create(AGraphics: TGraphics); begin     FSelectCommand := TSelectCommand.Create(AGraphics);     FMoveCommand := nil; end; destructor TGraphicManipulator.Destroy; begin     FSelectCommand.Free;     ClearMoveCommand;     //---     inherited; end; procedure TGraphicManipulator.MouseDown(X,Y: Integer); begin     with FSelectCommand do     begin         Position := Point(X,Y);         Execute;     end; end; procedure TGraphicManipulator.MouseMove(X,Y: Integer); begin end; procedure TGraphicManipulator.MouseUp(X,Y: Integer);     //---     procedure _HandleMoveCommand(ATarget: TGraphic);     begin         ClearMoveCommand;         //---         FMoveCommand := TMoveCommand.Create(ATarget,Point(X - ATarget.Position.X,Y - ATarget.Position.Y));         FMoveCommand.Execute;     end; begin     with FSelectCommand do     begin         if CurGraphic <> nil then         begin             with CurGraphic do             begin                 if (Position.X <> X) or (Position.Y <> Y) then                     _HandleMoveCommand(CurGraphic);             end;             //---             CurGraphic := nil;         end;     end; end; initialization     FConstraintSolver := nil; finalization     if FConstraintSolver <> nil then         FConstraintSolver.Free; end. unit Unit1; interface uses     Windows,Messages,SysUtils,Variants,Classes,Graphics,Controls,Forms,     Dialogs,uGraphic,StdCtrls; type     TForm1 = class(TForm)         Button1: TButton;         Button2: TButton;         procedure Button1Click(Sender: TObject);         procedure Button2Click(Sender: TObject);         procedure FormDestroy(Sender: TObject);         procedure FormCreate(Sender: TObject);         procedure FormMouseDown(Sender: TObject; Button: TMouseButton; Shift:             TShiftState; X,Y: Integer);         procedure FormMouseUp(Sender: TObject; Button: TMouseButton; Shift:             TShiftState; X,Y: Integer);     private         FGraphics: TGraphics;         FGraphicManipulator: TGraphicManipulator;     public     { Public declarations }     end; var     Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin     FGraphics := TGraphics.Create;     FGraphicManipulator := TGraphicManipulator.Create(FGraphics);     TConstraintSolver.Instance.Canvas := self.Canvas; end; procedure TForm1.FormDestroy(Sender: TObject); begin     FGraphics.Free;     FGraphicManipulator.Free; end; procedure TForm1.Button1Click(Sender: TObject);     //---     function _CreateGraphic(APosition: TPoint): TGraphic;     begin         Result := TGraphic.Create(self.Canvas,APosition);         Result.Draw;         FGraphics.Add(Result);     end; var     AStartConnection,AEndConnection: TGraphic; begin     AStartConnection := _CreateGraphic(point(20,20));     AEndConnection := _CreateGraphic(point(20,100));     with TConstraintSolver.Instance do     begin         AddConstraint(AStartConnection,AEndConnection);         Solve;     end; end; procedure TForm1.Button2Click(Sender: TObject); begin     with FGraphicManipulator do     begin         if MoveCommand <> nil then             MoveCommand.Unexecute;     end; end; procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton; Shift:     TShiftState; X,Y: Integer); begin     FGraphicManipulator.MouseDown(X,Y); end; procedure TForm1.FormMouseUp(Sender: TObject; Button: TMouseButton; Shift:     TShiftState; X,Y: Integer); begin     FGraphicManipulator.MouseUp(X,Y); end; end.


    最新回复(0)