エディタに何行表示されているのか知りたい(またはリサイズされた時のイベントが欲しい)

  1. 年末に話題になっていたミニマップをプラグインで作れるのではないかと思い、いろいろ実験中です。
    そこで、エディタに何行表示されているのかを知りたくなるわけですが、TSynEditにはLinesInWindowというプロパティがありますけれども、Meryさんにはないので、こんな関数を作ってみました。

    function TMainForm.LinesInWindow: NativeInt;
    var
    ScrollPos, ScrollMax: NativeInt;
    p: TNativePoint;
    begin
    Editor_GetScrollPos(FEditor, @p);
    ScrollPos := p.Y;
    p.Y := MaxInt;
    Editor_Redraw(FEditor, False);
    Editor_SetScrollPos(FEditor, @p);
    Editor_GetScrollPos(FEditor, @p);
    ScrollMax := p.Y - FScrollMargin;
    p.Y := ScrollPos;
    Editor_SetScrollPos(FEditor, @p);
    Editor_Redraw(FEditor, True);
    Result := Editor_GetLines(FEditor, POS_VIEW) - ScrollMax;
    end;

    陰でこっそりスクロールしてみるとか、どうにもイケてないです。スクロールを同期するために、EVENT_CARET_MOVEDやEVENT_SCROLLのたびにこの関数が呼ばれまくるので、重そうです。しかも、テキスト量が少なくて下に余白がある場合は測定不能ですし。
    そこで、
    function Editor_GetLinesInWindow(hwnd: THandle): NativeUInt;
    みたいのが欲しいなぁと思ったのでした。
    procedure Editor_GetScrollMax(hwnd: THandle; pptPos: PNativePoint);
    みたいのでもいいですが。

    しかし、よく考えてみると、ウィンドウをリサイズするか上下分割する時くらいしか変化しないデータなので、毎回測定する必要はないわけで、エディタがリサイズされた時のイベントがあれば、こんなのでもいいのかなぁと思ったり(下に余白がある場合に測定不能な問題は残りますが)。

     |  honadaiku  |  返信
  2. こんばんは、お返事遅くなってすみません^^;

    > TSynEditにはLinesInWindowというプロパティがありますけれども

    おぉ!Delphi ユーザ様ですか、ありがたやー!

    なるほど、1 ページにおさまる行数を取得するメソッドですか、内部ではそういう数値も持っていますのでプラグインから取得する API を用意するのは割と簡単ではあります。が、プラグインの API を更新したら github にあげてる SDK やらプラグインやらのソースを更新しなきゃいけないのが面倒くさいっていう…(ぉぃ

    プラグインでミニマップを実装するにはそもそも構文解析の色分けをプラグイン側から知るすべがないので、カラー表示が厳しそうですね。

    ご意見は今後の開発の参考にさせていただきたいと思います。

     |  Kuro  |  返信
  3. # 出遅れ感満載ですが

    > エディタに何行表示されているのか知りたい(またはリサイズされた時のイベントが欲しい)
    Delphi だとどうなるかわかりませんが、C と Windows SDK での話をします。

    ・GetScrollInfo() でスクロールバー情報を取得
     ⇒今のスクロール位置とページ量がわかるので、minimap 表示には足りる?
    ・SetWindowLongPtr() でコントロールのメッセージを横取り
     ⇒リサイズから何から色々情報をとれる

    ということで、正規でない方法であればやりようはあるかなーとおもいます。

    > プラグインでミニマップを実装するにはそもそも構文解析の色分けをプラグイン側から知るすべがないので、カラー表示が厳しそうですね。
    ini に書き込まれている謎の文字列を解析すれば何とかなりそうな……
    でも自力で構文解析やるとか、あまり考えたくないですね。

    ちょっと minimap 実装は考えてみましたが、汎用に作ろうと思うと如何に minimap の更新を少なく効率化するか、というところで詰まるんですよね。
    変更イベントはあるんですが、どの程度の変更があったかがわからないので、テキスト全取得を毎度やるしかないのか……とか。

    # 「プログラミング言語ランキング 1月版」曰く
    # Delphi は 11 位で Ruby より上……だと?
    # http://news.mynavi.jp/news/2017/01/10/083/

     |  ks  |  返信
  4. おはようございます、返信遅れてすみません。

    > ・GetScrollInfo() でスクロールバー情報を取得
    > ・SetWindowLongPtr() でコントロールのメッセージを横取り

    確かに、この辺りを駆使すればなんとかなりそうですね。

    > 変更イベントはあるんですが、どの程度の変更があったかがわからないので、テキスト全取得を毎度やるしかないのか……とか。

    アウトラインプラグイン (オプソ) で実装しているように、必要に応じてテキスト全取得をして、内部でそれをリストとして保持しておき、都度、そのリストから変更箇所を計算して非同期で部分的に更新する方式だと、負荷を軽減できるかもしれません。

    > # 「プログラミング言語ランキング 1月版」曰く
    > # Delphi は 11 位で Ruby より上……だと?

    無料版のおかげでしょうかw

    最近の Delphi は FireMonkey (MS で言うところの WPF みたいなヤツ) の完成度もマシになってきて、結構自由な GUI が作れるようになったので楽しいですね。Mery そっちのけで、もっぱら FireMonkey の勉強しております。

    Mery も FireMonkey で作っていれば他のプラットフォームへも簡単に移植できたのにー…。

     |  Kuro  |  返信
  5. ksさん、Kuroさん、こんにちは。

    > > ・GetScrollInfo() でスクロールバー情報を取得
    > > ・SetWindowLongPtr() でコントロールのメッセージを横取り
    >
    > 確かに、この辺りを駆使すればなんとかなりそうですね。

    Delphiが便利すぎて、たいていはDelphiに用意されたものだけで間に合ってしまうので、APIを駆使して直接調べるという発想がありませんでした。
    ksさん、有用なヒントをありがとうございました。

    > > 変更イベントはあるんですが、どの程度の変更があったかがわからないので、テキスト全取得を毎度やるしかないのか……とか。

    実は、マークダウンプレビューのプラグインも作りかけておりまして、EVENT_CHANGEDのたびにテキスト全取得してJavaScriptでHTML変換とハイライトをさせるという富豪仕様ですが、あまり長文でなければ、そこそこ実用的な速度で動くようです。

    > アウトラインプラグイン (オプソ) で実装しているように、必要に応じてテキスト全取得をして、内部でそれをリストとして保持しておき、都度、そのリストから変更箇所を計算して非同期で部分的に更新する方式だと、負荷を軽減できるかもしれません。

    おー、なるほどー。ソースを拝見して勉強させていただきます。

    > Mery も FireMonkey で作っていれば他のプラットフォームへも簡単に移植できたのにー…。

    TNotePadを移植するのが大変そうですが、元々Delphi製なので、それほどでもないのかな?

     |  honadaiku  |  返信
スポンサーリンク