テキスト子ウィンドウでフォントパーツを選択したら、メイン編集画面に編集のイメージカーソルが表示されるようになりました。今回はカーソル位置に設置イメージを表示する仕組みについて、簡単に説明していきます。
テキストカーソル
カーソル位置にイメージを表示する時は、最初に表示しても良いタイミングか確認します。この確認項目が結構多いです。
  • 自分がアクティブか
  • 選択を開始する待ち状態か
  • 選択実行中か
  • スクロール中か
  • ウィンドウがリサイズ中か
  • Altキーが押されているか
編集カーソルは設置可能かどうかを示す指標となりますので、編集が出来ない時は非表示にするという仕様としています。そのため、チェック項目が多岐にわたっています。実はこの他にも、ワイヤの設置中か等のモード別の判定もありますが、長くなるので今回はこのぐらいで💦

無事、編集カーソル表示状態と判定されたら、続いて編集モード別にカーソル表示処理を分岐します。テキストカーソル以外は既に実装済みなので、editMode の分岐に case EditMode.Text: を追加するだけです。

switch (editMode) { case EditMode.BG: DrawCursorGround(g); break; case EditMode.Object: DrawCursorObject(g); break; case EditMode.Attribute: DrawCursorAtrb(g); break; case EditMode.Wire: DrawCursorWire(g); break; case EditMode.Text: DrawCursorText(g); break; case EditMode.Paste: DrawCursorPaste(g); break; default: break; }

DrawCursorText メソッドでテキストカーソルを表示します。最初に判定すべきは、子ウィンドウ側でフォントパーツが選択されているかどうかです。こちらも判定プロパティを既に用意してありますので、そちらを確認するだけとなります。選択されていなければ、何もせず戻します。

if (!mcText.IsValidSelect) return;

表示範囲も他モードのカーソル表示実装時にメソッドとして用意してありますので、それを呼び出して取得します。

var pos = GetCharaCursorPos(); var rcDraw = GetDrawRect(new(pos.X, pos.Y, 1, 1));

この rcDraw が現在の編集エリアで拡大縮小された1枠の位置と大きさになります。この場所に A という文字を表示させたいのですが、その前に最後の表示チェックがあります。表示メニューで編集カーソル非表示を指定している可能性があります。また、イメージは点滅させるため、表示タイミングかどうかも確認します。どちらにも該当して、初めて表示可能と判定します。これらの判定も既にプロパティが用意されています。IsViewEditCursor と IsViewEditTiming です。この二つのプロパティを AND 判定して最終確認しています。
※ 廊下にこういうマット敷くの格好いいなあ。前向きに導入を検討するか…
表示タイミングであれば、リソース管理クラスから画像を取り出します。通常は受け取った画像は Dispose するべきですが、本マップエディタでは実行速度を稼ぐため、お漏らししないよう気をつけながら、極力各クラスで保存されているそのままのデータ参照としています。そのため、受け取り側で Dispose すると簡単に壊れます。悩んだ挙げ句の実装なのですが、まあ普通はこういう実装は止めた方が良いと思います💦
※ ゲーム系では良くあるのですが、システム系では御法度です…

あとは描画クリッピング設定をして実際に表示します。テキストカーソル表示メソッド全体は以下の通りです。

//--------------------------------------------------------------- // テキストカーソルを表示する //--------------------------------------------------------------- private void DrawCursorText(Graphics g) { if (!mcText.IsValidSelect) return; var pos = GetCharaCursorPos(); var rcDraw = GetDrawRect(new(pos.X, pos.Y, 1, 1)); if (IsViewEditCursor && IsViewEditTiming) { var bmp = RscText.GetFont(mcText.SelectID).GetTextPicture("A")[0]; using var path = new GraphicsPath(); path.AddRectangles(GetViewEditRects()); g.SetClip(path); g.DrawImage( bmp, rcDraw, new Rectangle(0, 0, bmp.Width, bmp.Height), GraphicsUnit.Pixel); g.ResetClip(); } else { g.FillRectangle(brTransDark, rcDraw); } g.DrawRectangle(PenCursor, rcDraw.X, rcDraw.Y, rcDraw.Width, rcDraw.Height); }

いろいろメソッドを用意済みなので、とてもシンプル(?)ですね。mcText.SelectID の値は、その前に mcText.IsValidSelect でチェックしているので保証されています。また選択されているので、RscText.GetFont() でフォントが取れる事も保証されます。このオブジェクトは破棄不要なので、そのまま直接使って GetTextPicture でイメージ画像を受け取ります。受け取るイメージは "A" という指定です。等幅だと1文字ずつ画像データがリストとしてきます。プロポーショナル指定だと、全ての文字が接続された1枚の画像として取得されます。どのみち、最低でも1枚は画像があるので、その先頭の画像だけ取りだして表示するという流れです。

ここまでいろいろ保証されているので、値の整合性チェックは不要なのですね。実行時チェックが一番重いので、必要最小限にして実行速度を稼ぐようにしています。あと、フォントの縦横比は無視して、枠内いっぱいに広げて描画としています。これは、大事なのは編集対象位置に、どんなフォントでテキストを設置しようとしているかであり、設置イメージではないので、このような表示としています。フォント縦横比を守ったのも試したのですが、やたらと細いのとか太いのだと逆に見づらかったので…。

ということで、テキスト設置の前段階までは出来ました。次からいよいよテキスト配置処理です。これは本当は実イメージのまま編集したいのですが、難しければ、入力ダイアログ出す事になります。はたして?
※ 以前、椅子のコロでフローリングに深刻なダメージを与えてしまい、それ以降はマットを使用しています。これは吸着タイプなのでかなり良さそうです。