imgui で GUI を作るときのメモ

imgui でこんなのを実現したいときはどうしたらいいかしらん... のメモです.

## 対象 ImGui version

1.71(or 1.72 WIP)

## Docking window にしたい.

ImGui はデフォルトではフリーレイアウトです.


https://github.com/ocornut/imgui/issues/2109

`docking` branch でドッキング対応が作業中です. 使っている感じではほぼ問題なく動いています.

`ImGuiConfigFlags_DockingEnable` を指定すると自動でドッキングが有効になります.

```
  ImGui::CreateContext();
  auto& io = ImGui::GetIO();

  // Enable docking(available in imgui `docking` branch at the moment)
  io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
```

https://github.com/BentleyBlanks/imguiDock

など外部の docking 対応機能も試しましたが, 不都合が多いでした(ので, ImGui `docking` branch がおすすめです)

## ノードエディタがほしい

https://github.com/thedmd/imgui-node-editor

C++14 になりますが, `imgui-node-editor` がいい感じです.


## 3D ギズモがほしい

Maya とか Blender とかで, translate/rotate/scale で出てくるマニピュレータです.

https://github.com/CedricGuillemet/ImGuizmo

ImGuizmo でいけます.

## カラーピッカーのカラースペースは?

現状 ImGui は degamma/ガンマ補正などは考慮していないようです.

https://github.com/ocornut/imgui/issues/578

レンダラ用の UI を作る場合. sRGB/Linear 対応などを自前対応が必要になります.

## 文字に背景色を指定したい

https://github.com/ocornut/imgui/issues/1566

直接背景色を指定する API はありません.
AddRectFilled() などで一旦矩形を描画したのち, 文字を描画します.


```
    ImVec2 bmin = //テキストの描画位置. ImGui::GetCursorScreenPos() などで取得

    ImVec2 text_size = ImGui::CalcTextSize(buf);

    ImVec2 fill_bmin = ImVec2(bmin.x - 4, bmin.y - 4);
    ImVec2 fill_bmax =
            ImVec2(bmin.x + text_size.x + 4, bmin.y + text_size.y + 4);

    // Draw quad for background color
    ImGui::GetWindowDrawList()->AddRectFilled(
            fill_bmin, fill_bmax,
            ImGui::GetColorU32(ImVec4(0.2f, 0.2f, 0.2f, 0.4f * alpha)),
            /* rounding */ 4.0f);


    ImGui::SetCursorScreenPos(bmin);

    ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.8f, alpha), "%s", buf);
```

## フォントをソースコードに埋め込みたい

`.ttf` ファイルを読み込んでだと, ファイルの探索に失敗したりとかで管理が面倒なのでバイナリに埋め込みたい.

https://github.com/ocornut/imgui/blob/master/misc/fonts/binary_to_compressed_c.cpp

こちらでフォントデータを unsigned int の配列にしてくれます. 圧縮/非圧縮選べます.

```
  io.Fonts->AddFontFromMemoryCompressedTTF(roboto_mono_compressed_data,
                                           roboto_mono_compressed_size,
                                           default_font_scale, &roboto_config);

```

などとしてメモリからフォントデータを読み込みます.

## 形状を描画したい.

Sphere, line, etc.. など. 

DrawList を取得して, `AddRect()` などを呼びます.

より複雑な形状を描画したり, antialiased line が欲しければ NanoVG あたりを使うのがよいかもれません.

## 大量のオブジェクトを描画したい

Imgui ではデフォルトでは頂点インデックスは 16bit のため, 大量にオブジェクトを描画するとインデックスが足りない assert が出たりします.

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/syoyo?ref_src=twsrc%5Etfw">@syoyo</a> Hello! About &quot;大量のオブジェクトを描画したい&quot; in <a href="https://t.co/AM5OOwvkcR">https://t.co/AM5OOwvkcR</a>, since 1.71 most default back-ends support ImDrawCmd::VtxOffset which allow larger single-window contents with 16-bit indices, see: <a href="https://t.co/55za53P06x">https://t.co/55za53P06x</a></p>&mdash; Omar (@ocornut) <a href="https://twitter.com/ocornut/status/1151173378734116864?ref_src=twsrc%5Etfw">July 16, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

Omar 先生からアドバイスいただきました. ありがとうございます.

1.71 から実装された VtxOffset feature を使うと 16bit index でも大量プリミティブを描画できるようになります. example の GL3 backend では VtxOffset 機能がデフォルトで有効になっています(`glDrawElementsBaseVertex()` を使っている)

OpenGL 2.x や, GLES では, `glDrawElementsBaseVertex` 相当を CPU 実装することで対応できるでしょう.

他に, imconfig.h でインデックスを 32bit にしたり, 自前描画や NanoVG を使うのも考えてみましょう.
 
## 画像を表示したい

`ImGui::Image` でいけます.

OpenGL で描画する場合, たとえば 8K 画像を一旦 GL テクスチャにするところでかなり時間がかかったりします(e.g. ドライバ内部とか). 

サムネイル表示ができればよいのであれば, 一度画像を縮小してからテクスチャを作るのがよいです.

## 画像だけスクロールさせたい　

画像が大きかったり, 拡大表示したいので Window に入りきらない.
デフォルトでは Window 全体にスクロールバーがつくので, 他の text や button などもスクロールしてしまう.
Window に画像(Image)をいれ, Image だけスクロールバーをつけたい.

=> `ChildWindow` でとりあえずできはしますが, macOS だと拡大が大きくなるとおかしくなるのを観測しました. 安定を求めるのであれば, 現状は画像ごとに Window を作るしかなさそうです.

## InputFloat などで, リターン打ったときのみアップデートしたい.

デフォルトでは数値を入力するたびに return true(update)になります.
たとえばカメラ位置などを扱っているばあい, 

```
1.0
->
0
->
0.5
``` 

みたいに値を削って 0.5 にしたいときに 0.0 の状態でもアップデート要求がでて, レンダーし直しになって面倒です.

InputTextFlags で

`ImGuiInputTextFlags_EnterReturnsTrue`

を指定することで, リターンを打ったときのみ true が帰るようにできます.

ただ, decimal precision も指定する必要がでてきますので注意です.


## オシャンティな progress 表示したい

https://github.com/ocornut/imgui/issues/1901

https://github.com/ocornut/imgui/issues/2020

## Table で Cell に値を入力したい

Table 自体は Canvas なため, text や button など埋め込むことができます.
したがって InputFloat など埋め込めば値入力が可能になります.

値を入力できるようにするには以下のような感じにします.
InputFloat の Label 文字列が同じだとおかしくなってしまうので, PushID/PopID で一意になるようします.
(もしくは, 一意になるような文字列を割り当てるかでしょうか)

`##` をつけると非表示のラベルになります.

```
        if (ImGui::BeginTable("table1", 3))
        {
            for (int row = 0; row < 4; row++)
            {
                ImGui::TableNextRow();
                for (int column = 0; column < 3; column++)
                {
                    ImGui::TableSetColumnIndex(column);
                    //ImGui::Text("Row %d Column %d", row, column);

                    ImGui::PushID(row * 3 + column);
                    ImGui::InputFloat("##v", &values[row][column]);
                    ImGui::PopID();
                }
            }
            ImGui::EndTable();
        }
```

![Screenshot from 2021-12-28 16-07-24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36275/3129295d-2499-5871-2245-0c0fcc361dc4.png)

Voila!
(TODO: InputFloat の background color を消す)
 
## グラフのプロットをしたい

implot があります!

https://github.com/epezent/implot

3D グラフはでも無いので, 自前で頑張るしかないかも.

## その他 UI component 集(awesome list)

https://github.com/TimoSalomaki/awesome-dear-imgui

でいろいろ見つかります