シンプルな Delphi ペイントで元に戻す操作とやり直し操作を実行できるようにする必要があります。そこで、履歴を保存するためのコンテナーを作成することにしました (完全な履歴ではなく、以前のビットマップ ファイルの一部のみ)。
unit HistoryQueue;
interface
uses
Graphics;
type
myHistory = class
constructor Create(Size:Integer);
public
procedure Push(Bmp:TBitmap);
function Pop():TBitmap;
procedure Clean();
procedure Offset();
function isEmpty():boolean;
function isFull():boolean;
function getLast():TBitmap;
protected
end;
var
historyQueueArray: array of TBitmap;
historyIndex, hSize:Integer;
implementation
procedure myHistory.Push(Bmp:TBitmap);
var tbmp:TBitmap;
begin
if(not isFull) then begin
Inc(historyIndex);
historyQueueArray[historyIndex]:=TBitmap.Create;
historyQueueArray[historyIndex].Assign(bmp);
end else begin
Offset();
historyQueueArray[historyIndex]:=TBitmap.Create;
historyQueueArray[historyIndex].Assign(bmp);
end;
end;
procedure myHistory.Clean;
var i:Integer;
begin
{ for i:=0 to hSize do begin
historyQueueArray[i].Free;
historyQueueArray[i].Destroy;
end; }
end;
constructor myHistory.Create(Size:Integer);
begin
hSize:=Size;
SetLength(historyQueueArray, hSize);
historyIndex:=-1;
end;
function myHistory.isEmpty: boolean;
begin
Result:=(historyIndex = -1);
end;
function myHistory.isFull: boolean;
begin
Result:=(historyIndex = hSize);
end;
procedure myHistory.Offset;
var i:integer;
begin
//historyQueueArray[0]:=nil;
for i:=0 to hSize-1 do begin
historyQueueArray[i]:=TBitmap.Create;
historyQueueArray[i].Assign(historyQueueArray[i+1]);
end;
end;
function myHistory.Pop: TBitmap;
var
popBmp:TBitmap;
begin
popBmp:= TBitmap.Create;
popBmp.Assign(historyQueueArray[historyIndex]);
Dec(historyIndex);
Result:=popBmp;
end;
function myHistory.getLast: TBitmap;
var
tBmp:TBitmap;
begin
tBmp:= TBitmap.Create;
tBmp.Assign(historyQueueArray[historyIndex]);
Result:=tBmp;
end;
end.
私のプログラムでは、そのように使用しています。
履歴に保存:
procedure TMainForm.FormCreate(Sender: TObject);
begin
{...}
picHistory:=myHistory.Create(10); //FOR UNDO
tempHistory:=myHistory.Create(10); //FOR REDO
end;
//if mouse is up - that mean we finish to draw something on canvas, so we gonna save what we drew
procedure TMainForm.imgMainMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var bmp:TBitmap;
begin
mouseIsDown:=false;
bmp:=TBitmap.Create;
try
bmp.Assign(imgMain.Picture.Bitmap);
picHistory.Push(bmp);
finally
bmp.Free;
end;
end;
そして取り消しとやり直し。
procedure TMainForm.btnUndoClick(Sender: TObject);
var redBmp:TBitmap;
begin
if(not picHistory.isEmpty) then begin //if we draw something before
//prepare to save what've done into history for redo
redBmp:=TBitmap.Create;
redBmp.Assign(picHistory.getLast);
//showing what were done with image before on screen
MainForm.imgMain.Canvas.Draw(0,0, picHistory.Pop);
//but in case we want to be able get back our last changes we save it into redo history
tempHistory.Push(redBmp);
redBmp.Free;
end;
end;
{...}
procedure TMainForm.btnRedoClick(Sender: TObject);
begin
//if there were something into history for redo then show int on canvas
if(not tempHistory.isEmpty) then
MainForm.imgMain.Canvas.Draw(0,0, tempHistory.Pop);
end;
しかし、奇妙なことが起こります - 私が押してUndo
も何も変わらないのです。そして、私が押すと、Redo
のように機能しUndo
ます。
ちなみに、redo と undo の履歴を異なる長さで宣言すると、このようになります。
procedure TMainForm.FormCreate(Sender: TObject);
begin
{...}
picHistory:=myHistory.Create(6); //FOR UNDO
tempHistory:=myHistory.Create(12); //FOR REDO
end;
そして、その中で何が起こるかは、picHistory
6ではなく配列の長さのように見えます.12です! したがって、これら 2 つのオブジェクトは 1 つの同じ配列を使用していると思います。なぜそれが起こり、どのようにそれを正しくするのですか?