Skip to content

Instantly share code, notes, and snippets.

@usagi
Last active September 23, 2016 22:57
Show Gist options
  • Save usagi/aaa92de1a93e8a6c15b16763258d9ff0 to your computer and use it in GitHub Desktop.
Save usagi/aaa92de1a93e8a6c15b16763258d9ff0 to your computer and use it in GitHub Desktop.
ImGui を使い"コンテキストメニュー"をもう少し"高度"にステートフルなオールインワン実装する例を紹介したい ref: http://qiita.com/usagi/items/671bc7c573a600819ac3
std::string label = "hoge";
std::string id = "xyz";
ImVec2 size;
auto clicked =
ImGui::Button
( ( id.empty() ? label : label + "###" + id ).c_str() // param-1 label & id
, size
);
struct button
{
std::string label;
std::string id;
std::function< auto () -> void > on_click; // †1
ImVec2 size;
auto operator()() const
{
if
( ImGui::Button
( ( id.empty() ? label : label + "###" + id ).c_str()
, size
)
)
on_click();
}
};
if ( ImGui::BeginXXX( ... ) )
{
...
ImGui::EndXXX( ... );
}
auto operator()() const
{
// 第1階層のメニュー定義のルートノードを開始
if ( ImGui::BeginMenu( ROOT, ... ) )
{
// 第1階層に末端のメニューノード A を定義
if ( ImGui::MenuItem( ROOT-A, ... ) )
on_click( ROOT-A, ... );
// 第1階層から第2階層へネストを展開する中間のメニューノード B を定義
if ( ImGui::BeginMenu( ROOT-B ... ))
{
// 第2階層に末端のメニューノード ROOT-B-X を定義
if ( ImGui::MenuItem( ROOT-B-X, ... ) )
on_click( ROOT-B-X, ... );
// 第1階層から第2階層へネストしたメニュー定義 ROOT-B を終了
ImGui::EndMenu( ... );
}
// 第1階層のメニュー定義のルートノードを終了
ImGui::EndMenu( ... );
}
}
struct statefull_menu_type
{
// ctor: https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L28
statefull_menu_type( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L37
auto operator()( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L149
auto show( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L65
auto clear( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L78
auto remove( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L83
auto add( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L102
auto modify( ... );
private:
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L155
auto _split( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L162
auto _generate_random_number( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L182
auto _remove( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L225
auto _add( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L259
auto _recursive_menu_render( ... );
// https://github.com/usagi/usagi/blob/e11b5ee19ef8c79ba9a5387407c1a4fc17bb9d8f/include/usagi/imgui/statefull_menu_type.hxx#L170
struct recursive_mapper_type;
};
using usagi::imgui::statefull_menu_type;
// テストコードの例として簡単のため static にオブジェクトを定義する。
// 実際にはクラスのメンバー変数にする事が多いだろう。
static statefull_menu_type o;
static statefull_menu_type n;
static statefull_menu_type q;
// テストコードの例として簡単のためメニュー定義が空ならば初期化するよう定義する。
// 実際にはクラスの initialize タイミングで行う事が多いだろう。
if ( q.empty() )
// メニューの階層構造は ImGui らしい便利さを優先したい事が多いだろうから、
// テキストから簡単に階層構造を定義できるようにしてみた。
// また、コンテキストメニューは末端のノードがクリックされた際に何らかのイベントを処理したい用途が通常なので、
// イベントはラムダ式で放り込めるようにしてみた。
// ImGui では callback 系に `std::function` を採用できない事情があるためステートレスでなければならないが、
// 今回はラッパークラスを定義して任意の C++ ファンクターを扱えるので便利も善い。
q.add
( decltype( q )::value_type{ "aaa/bbb/ccc" , [&]{ q.clear(); std::cerr << 0; } }
, decltype( q )::value_type{ "aaa/bbb/ddd" , []{ std::cerr << 1; } }
, decltype( q )::value_type{ "aaa/xxx/yyy/zzz" , []{ std::cerr << 2; } }
, decltype( q )::value_type{ "aaa/xxx/yyy/www" , []{ std::cerr << 3; } }
, decltype( q )::value_type{ "ppp/xxx/sss/ttt" , []{ std::cerr << 4; } }
, decltype( q )::value_type{ "ppp/xxx/sss/uuu/vvv", []{ std::cerr << 5; } }
, decltype( q )::value_type{ "qqq/xxx/sss/ttt" , [&]{ q.remove( "qqq/xxx/sss/ttt" ); std::cerr << 6; } }
, decltype( q )::value_type{ "qqq/xxx/sss/uuu/vvv", [&]{ q.remove( "qqq/xxx/sss/uuu/vvv" ); std::cerr << 7; } }
);
if ( n.empty() )
n.add
( decltype( n )::value_type{ "nnn/1/2/3", [&]{ std::cerr << "n3 "; } }
, decltype( n )::value_type{ "nnn/4/5/6", [&]{ std::cerr << "n6 "; } }
, decltype( n )::value_type{ "nnn/7/8/9", [&]{ std::cerr << "n9 "; } }
);
if ( o.empty() )
o.add
( decltype( o )::value_type{ "ooo/1/2/3", [&]{ std::cerr << "o3 "; } }
, decltype( o )::value_type{ "ooo/4/5/6", [&]{ std::cerr << "o6 "; } }
, decltype( o )::value_type{ "ooo/7/8/9", [&]{ std::cerr << "o9 "; } }
);
// 蛇足的な注意となるが、ここでもボタンの ID は重複してはならない。
if ( ImGui::Button( "open context menu 1" ) )
// 実際にはクラスの update 処理中などに何らかのトリガーに応じて呼ぶことになるだろう。
q.show();
if ( ImGui::Button( "open context menu 2" ) )
n.show();
if ( ImGui::Button( "open context menu 3" ) )
o.show();
// 実際にはクラスの render 処理中に定義する事になるだろ。
q();
n();
o();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment