ドット絵エディタを作る(Undo機能をつけるぞ編)
・・・う…うーん,飲みすぎ・・・
type TUndoData = record
bmp : TBitmap ;
width, height : Integer ;//(TBitmapが既にもってるから実際には必要なし)
type : Byte ;
end;
で,このパックしたデータを記録するためのリストを作成.std::listみたいなもんかね?


{
Undoデータを追加します.
・データ追加のタイミングは基本的にペンを上げた時です.(新規作成時も)
・Undoデータは最大数を超えないようにします.
・Undoデータ追加じは,FUndoIndex にセットされる値は,
データ数-1 と同じにします.
}
procedure TMDIChild.AddUndo;
var
undo_data : TChildUndoData ;
rc : TRect ;
i : Integer ;
begin
// 保存するデータが無い(既にクローズしてるとか)場合は抜ける.
if ( FBackBmp = nil ) then Exit ;
// 多くなりすぎた場合は古いのから開放する.
if ( FUndoList.Count >= UndoNumMax ) then begin
undo_data := FUndoList.Items[0] ;
undo_data.Free ;
FUndoList.Delete( 0 );
dec( FUndoIndex ); // 一旦減らす.
end;
inc( FUndoIndex ); // データが増えるからインクリメント♪
// index より多くても開放する.
// (リドゥが無い場合はこんなことしなくてもいいのにねぇ.)
i := FUndoIndex ;
while ( i <= FUndoList.Count-1 ) do begin
undo_data := FUndoList.Items[i] ;
undo_data.Free ;
undo_Data := nil ;
FUndoList.Items[i] := nil ; // Pack でまとめて消す.
inc(i);
end;
FUndoList.Pack ;
// 追加すべきデータを作る
SetRect( rc, 0, 0, FBackBmp.Width, FBackBmp.Height );
try
undo_data := TChildUndoData.Create ; // 開放はデストラクタでやる.
with undo_data do begin
Bmp.PixelFormat := FBackBmp.PixelFormat ;
Bmp.Width := FBackBmp.Width ;
Bmp.Height := FBackBmp.Height ;
Bmp.Canvas.CopyRect( rc, FBackBmp.Canvas, rc );
end;
FUndoList.Add( undo_data );
except
end;
// この段階で,FUndoIndex と FUndoList.Counter-1 は等しくなってるはずじゃが.
end;
{
Undo できるかチェック.
・データが無い場合
・ツールで処理中である場合
などは Undo できません.
}
function TMDIChild.CanUndo: Boolean;
begin
Result := False ;
if ( FUndoList.Count <= 0 ) then Exit ;
if ( FUndoIndex <= 0 ) then Exit ;
if ( g_ToolData.Processing ) then Exit ;
Result := True ;
end;
{
Undo実行
・読み出すデータの位置は,FUndoIndex-1 のデータです.
・Undo なら FUndoIndex-- します.
・Redo なら FUndoIndex++ します.
}
procedure TMDIChild.DoUndo;
var
undo_data : TChildUndoData ;
rc : TRect ;
begin
if not CanUndo then Exit ;
// こうなったらは本当はおかしい.
if ( FUndoIndex > FUndoList.Count-1 ) then begin
MessageDlg( 'Undoのつじつまがあいません', mtError, [mbOK], 0 );
FUndoIndex := FUndoList.Count-1 ;
end;
{ Undo データ取得 }
undo_data := FUndoList.Items[ FUndoIndex-1 ] ;
SetRect( rc, 0, 0, undo_data.Bmp.Width, undo_data.Bmp.Height );
FBackBmp.Width := undo_data.Bmp.Width ;
FBackBmp.Height := undo_data.Bmp.Height ;
FBackBmp.Canvas.CopyRect( rc, undo_data.bmp.Canvas, rc );
{ インデクスデクリメント }
dec( FUndoIndex );
if ( FUndoIndex < 0 ) then FUndoIndex := -1 ;
RedrawAll ; //再描画
end;
{
Redo できるか
・データが無い場合
・ツールで処理中である間
などは Redo できません.
}
function TMDIChild.CanRedo: Boolean;
begin
Result := False ;
if ( FUndoList.Count <= 0 ) then Exit ;
if ( FUndoIndex <= -1 ) then Exit ;
if ( g_ToolData.Processing ) then Exit ;
if ( FUndoIndex >= FUndoList.Count-1 ) then Exit ;//リドゥするデータがない.
Result := True ;
end;
{
Redo 実行
・Redo は UndoIndex+1 のデータを読み出します.
}
procedure TMDIChild.DoRedo;
var
undo_data : TChildUndoData ;
rc : TRect ;
begin
if not CanRedo then Exit ;
{ Redo データ取得 }
undo_data := FUndoList.Items[ FUndoIndex + 1 ];
SetRect( rc, 0, 0, undo_data.Bmp.Width, undo_data.Bmp.Height );
FBackBmp.Width := undo_data.Bmp.Width ;
FBackBmp.Height := undo_data.Bmp.Height ;
FBackBmp.Canvas.CopyRect( rc, undo_data.bmp.Canvas, rc );
{ インデクスインクリメント }
inc( FUndoIndex );
if ( FUndoIndex > FUndoList.Count -1 ) then FUndoIndex := FUndoList.Count -1 ;
RedrawAll ; //再描画
end;