ぬの部屋(仮)
nu-no-he-ya
  •       1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
       1234
    567891011
    12131415161718
    19202122232425
    26272829   
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28      
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
    1234567
    891011121314
    15161718192021
    22232425262728
           
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
         12
    3456789
    10111213141516
    17181920212223
    242526272829 
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
        123
    45678910
    11121314151617
    18192021222324
    25262728   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    15161718192021
    293031    
           
         12
    3456789
    10111213141516
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    30      
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    2627282930  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728     
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
        123
    45678910
    11121314151617
    18192021222324
    252627282930 
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
     123456
    78910111213
    14151617181920
    21222324252627
    28293031   
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
     123456
    78910111213
    14151617181920
    21222324252627
    282930    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
    31      
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
      12345
    6789101112
    13141516171819
    20212223242526
    27282930   
           
          1
    2345678
    9101112131415
    16171819202122
    23242526272829
    3031     
          1
    2345678
    9101112131415
    16171819202122
    232425262728 
           
       1234
    567891011
    12131415161718
    19202122232425
    262728293031 
           
    1234567
    891011121314
    15161718192021
    22232425262728
    293031    
           
         12
    3456789
    10111213141516
    17181920212223
    24252627282930
           
      12345
    6789101112
    13141516171819
    20212223242526
    2728293031  
           
    1234567
    891011121314
    15161718192021
    22232425262728
    2930     
           
        123
    45678910
    11121314151617
    18192021222324
    25262728293031
           
  • Win32APIでIMEを使用(2)

    WM_IME_COMPOSITIONはlParamによって取得できる情報を区別している。特にGCS_COMPATTRは変換中の文字列の一文字ずつの情報を扱うのでATTR_*でさらに分岐が必要。

     

    #pragma warning(disable:4996)
    
    #include <windows.h>
    #include <imm.h>
    #pragma comment(lib, "imm32.lib")
    
    #include <string>
    
    // https://learn.microsoft.com/ja-jp/windows/win32/intl/ime-composition-string-values
    
    
    // 変換中の文字列 GCS_COMPSTR
    void test_GCS_COMPSTR(HIMC hIMC, HDC hdc) {
    
        DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_COMPSTR, NULL, 0);
        if (inputBytes) {
    
            size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数
            wchar_t* buffer = new wchar_t[U16Count];
    
            // 確定した文字列を取得
            ImmGetCompositionStringW(hIMC, GCS_COMPSTR, buffer, inputBytes);
            buffer[U16Count - 1] = L'\0';
    
            // 確定した文字列を描画
            int inx = 0;
            int iny = 0;
            TextOutW(hdc, inx, iny, buffer, wcslen(buffer));
    
            delete[] buffer;
    
        }
    
    }
    

    // 変換中の文字列の属性情報 GCS_COMPATTR
    void test_GCS_COMPATTR(HIMC hIMC, HDC hdc) {
        DWORD attr = ImmGetCompositionString(hIMC, GCS_COMPATTR, NULL, 0);
    
        BYTE* buffer = new BYTE[attr];// 変換中の文字数と同じ個数の要素を確保
    
        ImmGetCompositionString(hIMC, GCS_COMPATTR, buffer, attr);// 変換中の各文字ごとに状態情報が格納される
    
        char str[256] = {};
    
        for (size_t i = 0; i < 256; i++)
            str[i] = ' ';
        str[255] = '\0';
        TextOutA(hdc, 0, 30, str, strlen(str));
    
    
        size_t i;
        for (i = 0; i < attr; i++) {
            switch (buffer[i])
            {
            case ATTR_INPUT:
                str[i] = 'I';
                break;
            case ATTR_TARGET_CONVERTED:
                str[i] = 'T';
                break;
            case ATTR_CONVERTED:
                str[i] = 'C';
                break;
            case ATTR_FIXEDCONVERTED:
                str[i] = 'F';
                break;
            case ATTR_TARGET_NOTCONVERTED:
                str[i] = 'N';
                break;
            case ATTR_INPUT_ERROR:
                str[i] = 'E';
                break;
            }
    	}
        str[i] = '*';
        TextOutA(hdc, 0, 30, str, strlen(str));
    
    
        delete[] buffer;
    }
    

    // 変換中の文字列内のカーソル位置  
    void test_GCS_CURSORPOS(HIMC hIMC, HDC hdc) {
        LONG cursorPos = ImmGetCompositionString(hIMC, GCS_CURSORPOS, NULL, 0);
    
        char str[256] = {};
        sprintf(str, "cursorPos:%d", cursorPos);
    
        TextOutA(hdc, 0, 50, str, strlen(str));
    
    }
    

    // 変換開始位置 GCS_DELTASTART
    void test_GCS_DELTASTART(HIMC hIMC, HDC hdc) {
        LONG deltaStart = ImmGetCompositionString(hIMC, GCS_DELTASTART, NULL, 0);
    
        char str[256] = {};
        sprintf(str, "deltaStart:%d", deltaStart);
    
        TextOutA(hdc, 0, 90, str, strlen(str));
    
    }
    

    // GCS_RESULTSTR
    void test_GCS_RESULTSTR(HIMC hIMC,HDC hdc) {
        // GCS_RESULTSTR ... IMEの入力が確定されたフラグ
    
        // 確定した文字列のバイト数を取得(NULLは含まない)
        DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
        if (inputBytes) {
    
            size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数
            wchar_t* buffer = new wchar_t[U16Count];
    
            // 確定した文字列を取得
            ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buffer, inputBytes);
            buffer[U16Count - 1] = L'\0';
    
            // 確定した文字列を描画
            int inx = 100;
            int iny = 100;
            TextOutW(hdc, inx, iny, buffer, wcslen(buffer));
    
            delete[] buffer;
        }
    }
    

    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    
        HIMC hIMC;
    
        int inx = 100;
        int iny = 100;
        switch (message) {
        case WM_CREATE:
    
            // IMEを使用可能にする
            ImmAssociateContext(hWnd, ImmCreateContext());
    
    
            break;
    
    
        case WM_IME_STARTCOMPOSITION:
    
            OutputDebugString(L"IME 入力開始\n");
    
            hIMC = ImmGetContext(hWnd);  // IMEコンテキストを取得
            if (hIMC)
            {
                // IMEのウィンドウ位置を設定
                COMPOSITIONFORM cf2;
                cf2.dwStyle = CFS_POINT;
                cf2.ptCurrentPos.x = inx;
                cf2.ptCurrentPos.y = iny;
                ImmSetCompositionWindow(hIMC, &cf2); // IMEのウィンドウ位置を設定
                ImmReleaseContext(hWnd, hIMC);
            }
            break;
    
        case WM_IME_COMPOSITION:
    
            // IMEの入力中の処理
    
            hIMC = ImmGetContext(hWnd);
            if (hIMC) {
                HDC hdc = GetDC(hWnd);
                if (lParam & GCS_RESULTSTR) {
                    test_GCS_RESULTSTR(hIMC,hdc);
                }
                if (lParam & GCS_COMPSTR) {
                    test_GCS_COMPSTR(hIMC, hdc);
                }
                if (lParam & GCS_COMPATTR) {
                    test_GCS_COMPATTR(hIMC, hdc);
                }
                if (lParam & GCS_CURSORPOS)
                {
                    test_GCS_CURSORPOS(hIMC, hdc);
                }
                if (lParam & GCS_DELTASTART)
                {
                    test_GCS_DELTASTART(hIMC, hdc);
                }
                ReleaseDC(hWnd, hdc);
                ImmReleaseContext(hWnd, hIMC);
            }
            break;
    
        case WM_IME_ENDCOMPOSITION:
            OutputDebugString(L"IME 入力終了\n");
            break;
    
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return DefWindowProc(hWnd, message, wParam, lParam);;
    }
    

    Win32APIでIMEを使用(1)

    imm.h,imm32.libを読み込み、ImmAssociateContextなどを使ってIMEへアクセスする。

    #include <windows.h>
    #include <imm.h>
    #pragma comment(lib, "imm32.lib")
    
    
    
    LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    
        HIMC hIMC;
    
        int inx = 100;
        int iny = 100;
        switch (message) {
        case WM_CREATE:
    
            // IMEを使用可能にする
            ImmAssociateContext(hWnd, ImmCreateContext());
    
            break;
    

    case WM_IME_STARTCOMPOSITION:
            // IMEでの入力が開始される
    OutputDebugString(L"IME 入力開始\n"); hIMC = ImmGetContext(hWnd); // IMEコンテキストを取得 if (hIMC) { // IMEのウィンドウ位置を設定 COMPOSITIONFORM cf2; cf2.dwStyle = CFS_POINT; cf2.ptCurrentPos.x = inx; cf2.ptCurrentPos.y = iny; ImmSetCompositionWindow(hIMC, &cf2); // IMEのウィンドウ位置を設定 ImmReleaseContext(hWnd, hIMC); } break;

    case WM_IME_COMPOSITION:
            // IMEの入力中の処理
    
            hIMC = ImmGetContext(hWnd);
            if (hIMC) {
                if (lParam & GCS_RESULTSTR) {
                    // GCS_RESULTSTR ... IMEの入力が確定されたフラグ
    
                    // 確定した文字列のバイト数を取得(NULLは含まない)
                    DWORD inputBytes = ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, NULL, 0);
                    if (inputBytes) {
    
                        size_t U16Count = inputBytes / sizeof(wchar_t) + 1;// wchar_tでの、NULLを含んだ文字数
                        wchar_t* buffer = new wchar_t[U16Count];
    
                        // 確定した文字列を取得
                        ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, buffer, inputBytes);
                        buffer[U16Count - 1] = L'\0';
    
                        // 確定した文字列を描画
                        HDC hdc = GetDC(hWnd);
                        TextOutW(hdc, inx, iny, buffer, wcslen(buffer));
                        ReleaseDC(hWnd, hdc);
    
                        delete[] buffer;
                    }
                }
                ImmReleaseContext(hWnd, hIMC);
            }
            break;
    

    case WM_IME_ENDCOMPOSITION:
            OutputDebugString(L"IME 入力終了\n");
            break;
    
        case WM_DESTROY:
            PostQuitMessage(0);
            break;
    
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
        }
        return DefWindowProc(hWnd, message, wParam, lParam);;
    }
    
    int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) {
    
        WNDCLASS wc;
        wc.style = CS_HREDRAW | CS_VREDRAW;
        wc.lpfnWndProc = WndProc;
        wc.cbClsExtra = 0;
        wc.cbWndExtra = 0;
        wc.hInstance = hInstance;
        wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
        wc.hCursor = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
        wc.lpszMenuName = NULL;
        wc.lpszClassName = L"SZL-IME-TEST";
    
        RegisterClass(&wc);
    
        HWND hWnd = CreateWindowW(L"SZL-IME-TEST", L"title", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 640, 480, NULL, NULL, hInstance, NULL);
    
    
        ShowWindow(hWnd, nCmdShow);
        UpdateWindow(hWnd);
    
        MSG msg = {};
        while (GetMessage(&msg, NULL, 0, 0)) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    
        return (int)msg.wParam;
    }
    

    wxListBoxで自作ツリー構造を表示する

    tree.hpp

    #ifndef TREE_H
    #define TREE_H
    
    #endif //TREE_H
    
    #include <algorithm>
    #include <memory>
    
    using namespace std::literals::string_literals;
    
    // ノードを継承したクラスに asString() メンバ関数があるかを判定するコンセプト
    template<typename T>
    concept HasAsString = requires(T a) {
        { a.asString() } -> std::same_as<std::string>;
    };
    
    // ノードの基底クラス
    class NodeBase {
    protected:
    public:
        std::shared_ptr<NodeBase> parent;
    
        virtual ~NodeBase() = default;
        virtual std::string asString() const = 0;  // 必ず実装させる仮想関数
        virtual std::shared_ptr<NodeBase> getParent() const { return parent; }
        virtual bool isFolder() const = 0;
    };
    
    // フォルダクラス
    template <HasAsString T>
    class Folder : public NodeBase, public std::enable_shared_from_this < Folder<T> > {
        std::vector<std::shared_ptr<NodeBase>> children;  // 子ノードは NodeBase 型を保持
    
        T value;
        bool isOpen;
    public:
        Folder(const T& data) : value(data), isOpen(false) {}
    
        // 子ノードを追加するメソッド
        void add(const std::shared_ptr<NodeBase>& node) {
            children.push_back(node);
            node->parent = std::dynamic_pointer_cast<NodeBase>(this->shared_from_this());
        }
    
        const T& getName() const {
            return value;
        }
    
        std::string asString() const override {
            return value.asString();
        }
    
        const std::vector<std::shared_ptr<NodeBase>>& getChildren() const {
            return children;
        }
        std::vector<std::shared_ptr<NodeBase>>& getChildren() {
            return children;
        }
    
        bool getIsOpen() const {
            return isOpen;
        }
    
        void setIsOpen(bool open) {
            isOpen = open;
        }
    
        bool isFolder() const override {
    		return true;
    	}
    
    
    };
    
    // アイテムクラス
    // フォルダに入れるデータを保持するクラス
    template <HasAsString T>
    class Item : public NodeBase, public std::enable_shared_from_this < Item<T> > {
        T value;
    public:
        Item(const T& data) : value(data) {}
    
        std::string asString() const override {
            return value.asString();
        }
    
        bool isFolder() const override {
            return false;
        }
    };
    
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    
    // 全てのノードの数をカウントする関数
    template <HasAsString FT>
    int getAllNodeCount(const std::shared_ptr<NodeBase>& node) {
        int count = 1;  // 自分自身をカウント
    
        // フォルダの場合は、子ノードも再帰的にカウント
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            for (const auto& child : folder->getChildren()) {
                count += getAllNodeCount<FT>(child);
            }
        }
    
        return count;
    }
    
    
    
    // 開いているノードの数をカウントする関数
    template <HasAsString FT>
    int getOpenNodeCount(const std::shared_ptr<NodeBase>& node) {
        int count = 1;  // 自分自身をカウント
    
        // フォルダの場合、開いているかどうかを確認
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (folder->getIsOpen()) {
                // 開いている場合、子ノードも再帰的にカウント
                for (const auto& child : folder->getChildren()) {
                    count += getOpenNodeCount<FT>(child);
                }
            }
        }
    
        return count;
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    // 指定した index のノードを取得する関数の補助関数
    template <HasAsString FT>
    std::shared_ptr<NodeBase> getNodeHelper(const std::shared_ptr<NodeBase>& node, int& currentIndex, int targetIndex, bool open = false) {
        // 現在のノードをカウント
        if (currentIndex == targetIndex) {
            return node;
        }
        currentIndex++;
    
        // フォルダの場合は、開閉状態を確認し、再帰的に子ノードを探索
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (open || folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    auto result = getNodeHelper<FT>(child, currentIndex, targetIndex, open);
                    if (result) {
                        return result;
                    }
                }
            }
        }
    
        return nullptr; // 見つからなかった場合
    }
    
    // 指定した index のノードを取得する関数
    // ここでの index は、開いているノードのみでカウントする
    template <HasAsString FT>
    std::shared_ptr<NodeBase> getNodeOnlyOpen(const std::shared_ptr<NodeBase>& root, int targetIndex) {
        int currentIndex = 0; // ノードのカウント用
        return getNodeHelper<FT>(root, currentIndex, targetIndex, false);
    }
    
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    
    
    
    // ノードを削除する関数
    template <HasAsString FT>
    void deleteNode(std::shared_ptr<NodeBase> node) {
        if (auto parent = node->getParent()) {
            if (auto parentFolder = std::dynamic_pointer_cast<Folder<FT>>(parent)) {
                auto& siblings = parentFolder->getChildren();
                siblings.erase(std::remove(siblings.begin(), siblings.end(), node), siblings.end());
            }
        }
    }
    
    // 指定したノードの管理情報を取得する関数
    // 戻り値 1 親ノード
    // 戻り値 2 posで指定したノードを持つ親ノードの、子ノードリストのインデクス
    template <HasAsString FT>
    std::pair< std::shared_ptr<NodeBase>, int> GetNodeListPos(std::shared_ptr<NodeBase> root, int pos) {
    
        // posの位置の現在のノードを取得
        auto now_node = getNodeOnlyOpen<FT>(root, pos);
    
        // now_nodeの親ノードを取得
        auto parent_node = now_node->getParent();
    
        // now_nodeの親ノードの子ノードリストを取得
        auto parent_folder = std::dynamic_pointer_cast<Folder<FT>>(parent_node);
        auto& siblings = parent_folder->getChildren();
    
        // now_nodeの親ノードの子ノードリストのインデクスを取得
        auto it = std::find(siblings.begin(), siblings.end(), now_node);
        int index = std::distance(siblings.begin(), it);
    
    
        return { parent_node,index };
    
    }
    
    template <HasAsString FT, HasAsString IT>
    void addNodeUnder(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node) {
        // nodeをrootの最後に追加
        auto folder = std::dynamic_pointer_cast<Folder<FT>>(root);
        folder->getChildren().push_back(node);
        node->parent = root;
    
    }
    
    template <HasAsString FT, HasAsString IT>
    void addNodeHere(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node, int pos) {
        // ノードを追加する関数
        // 挿入位置は番号posで指定する
        // 新しく追加したアイテムが必ずpos番目になる
        // 必ず、追加前のposのノードと同じ階層に追加する
    
        // 番号posは開いているノードのみでカウントする
        // 例えばfolder21がノードを持っていても、folder21は開いていないのでその中のノードはカウントしない
        // 1 - folder1
        // 2   - folder2
        // 3      item21
        // 4      item22
        // 5      + folder21
        // 6   - folder3
        // 7      item31
        // 8      item32
        // 9      item33
    
        // getNode(folder1,3) == item21
        // この時、
        // 例1 addNode(NewFolder,3) で
        // 2   - folder2
        // 3      + NewFolder
        // 4      item21
        // 5      item22
    
    
        auto childinfo = GetNodeListPos<FT>(root, pos);
        std::shared_ptr<NodeBase> childlist = childinfo.first;
        auto index = childinfo.second;
    
        // nodeをchildlistのindex番に追加
        auto folder = std::dynamic_pointer_cast<Folder<FT>>(childlist);
        folder->getChildren().insert(folder->getChildren().begin() + index, node);
        node->parent = childlist;
    
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    struct TreeSliceItem {
        int pos;
        std::shared_ptr<NodeBase> node;
        int hierarchy;
    };
    template <HasAsString FT>
    void GetTreeSliceTraverse(const std::shared_ptr<NodeBase>& node, int hierarchy, int& currentIndex, int from, int to, std::vector<TreeSliceItem>& slice) {
        if (currentIndex >= from && currentIndex <= to) {
            slice.push_back({ currentIndex, node, hierarchy });
        }
        currentIndex++;
    
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    GetTreeSliceTraverse<FT>(child, hierarchy + 1, currentIndex, from, to, slice);
                }
            }
        }
    }
    
    // ツリーから一部を取り出す関数
    template <HasAsString FT>
    std::vector< TreeSliceItem> GetTreeSlice(std::shared_ptr<NodeBase> root, int from, int to) {
        // 例えば、以下の状態の時、
        // 1 - folder1
        // 2   - folder2
        // 3      item21
        // 4      item22
        // 5      + folder21
        // 6   - folder3
        // 7      item31
        // 8      item32
        // 9      item33
    
        // GetTreeSlice(folder1,3,7) で
        // 3      item21
        // 4      item22
        // 5      + folder21
        // 6   - folder3
        // 7      item31
        // を取得
    
        std::vector<TreeSliceItem> slice;
        int currentIndex = 0;
        int currentHierarchy = 0;
    
        GetTreeSliceTraverse<FT>(root, currentHierarchy, currentIndex, from, to, slice);
    
        return slice;
    
    }
    

    TreeSlice.hpp

    ツリーの一部を取得するクラスを取得するクラス。

    #include "tree.hpp"
     
    // ツリーの一部を取り出すクラス
    template
    <HasAsString FT, HasAsString IT> class TreeSlice { std::shared_ptr<Folder<FT>> _root; std::vector< TreeSliceItem> _items; int _size; int _pos; public: void SetTree(const std::shared_ptr<Folder<FT>>& r) { _root = r; } void SetSliceInfo(int size,int pos) { _size = size; _pos = pos; } void GetSlice() { _items = GetTreeSlice<FT>(_root, _pos, _pos + _size - 1); } const std::vector< TreeSliceItem>& GetItems() const { return _items; } };
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
     
    struct FolderData {
        std::string name;
        std::string asString() const {
            return name;
        }
    
        // コンストラクタ
        FolderData(const std::string& n) : name(n) {}
    
    };
    
          
    struct ItemData {
        std::string name;
        std::string asString() const {
            return name;
        }
    
        // コンストラクタ
        ItemData(const std::string& n) : name(n) {}
    
    };
    

    main.cpp

    #ifndef WX_PRECOMP
    #include <wx/wx.h>
    #endif
    
    #include <wx/gdicmn.h> // wxPointに必要
    #include <wx/frame.h>  // wxFrameに必要
    
    
    // wxBufferedPaintDC のinclude
    #include <wx/dcbuffer.h>
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    #include <string>
    #include <memory>
    #include <unordered_map>
    
    #include "tree_data.hpp"
    
    
    // データを作成
    std::shared_ptr<Folder<FolderData>> create_data() {
        // ツリー構造を作成
        auto root = std::make_shared<Folder<FolderData>>(FolderData{ "folder1" });
    
        for (size_t i = 0; i < 10; i++) {
            auto folder = std::make_shared<Folder<FolderData>>(FolderData{ "folder" + std::to_string(i) });
            root->add(folder);
            for (size_t j = 0; j < 10; j++) {
                auto item = std::make_shared<Item<ItemData>>(ItemData{ "item" + std::to_string(i) + std::to_string(j) });
                folder->add(item);
            }
        }
        root->setIsOpen(true);
    
        return root;
    }
    
          
    // リストボックスが一度に表示可能なアイテム数を取得
    int GetVisibleItemCount(wxListBox * listBox)
    {
        // リストボックスのDCを使って一行の高さを取得し
        // リストボックスのクライアントサイズから表示可能なアイテム数を計算する
    
    
        // リストボックスのクライアントサイズを取得
        wxSize clientSize = listBox->GetClientSize();
    
        // wxClientDCを使用してデフォルトのアイテム高さを取得
        wxClientDC dc(listBox);
        wxCoord textWidth, textHeight;
        // 指定した文字列の幅と高さを取得
        dc.GetTextExtent("TEXT", &textWidth, &textHeight);
    
        // 表示可能なアイテム数を計算
        int visibleItemCount = clientSize.GetHeight() / textHeight;
    
        return visibleItemCount;
    }
    

    // スクロールバー付きリスト
    class ExList : public wxPanel {
    
        int itemCount;// アイテム数
        int visibleItemCount;// 一度に表示できるアイテム数
    
        wxBoxSizer* mainSizer;
        wxScrollBar* scrollBar;
    
        // リストコントロール
        wxListBox* listBox;
    
        TreeSlice<FolderData, ItemData> _treeSlice;// リストの一部を取得
        std::shared_ptr<Folder<FolderData>> _root;// ツリーのルート
    
        std::vector< TreeSliceItem> _now_valid_items;// 表示する範囲のツリーのアイテム
    
        void PostCreate() {
    
    
            _root = create_data();// アイテム作成
            _treeSlice.SetTree(_root);// ツリーの一部を切り出す準備
    
            visibleItemCount = GetVisibleItemCount(listBox);// 表示可能なアイテム個数を更新
    
            disp();
    
        }
    
    public:
        // コンストラクタ
        ExList(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
            // メインSizer
            mainSizer = new wxBoxSizer(wxHORIZONTAL);
            this->SetSizer(mainSizer);
    
            // リストコントロール
            listBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr, wxLB_SINGLE);
            mainSizer->Add(listBox, 1, wxEXPAND | wxALL, 5);
    
            // スクロールバー
            scrollBar = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
            mainSizer->Add(scrollBar, 0, wxEXPAND | wxALL, 5);
    
    
            this->Layout();
            CallAfter(&ExList::PostCreate);
    
            // スクロールバーのイベント
            scrollBar->Bind(wxEVT_SCROLL_PAGEUP, &ExList::OnScrollPageUp, this);
            scrollBar->Bind(wxEVT_SCROLL_PAGEDOWN, &ExList::OnScrollPageDown, this);
            scrollBar->Bind(wxEVT_SCROLL_THUMBTRACK, &ExList::OnScrollThumbTrack, this);
            scrollBar->Bind(wxEVT_SCROLL_LINEUP, &ExList::OnScrollUp, this);
            scrollBar->Bind(wxEVT_SCROLL_LINEDOWN, &ExList::OnScrollDown, this);
    
            // リストボックスのアイテムをクリックした時のイベント
            listBox->Bind(wxEVT_LISTBOX, &ExList::OnListBox, this);
    
            // サイズイベント
            this->Bind(wxEVT_SIZE, &ExList::OnSize, this);
    
        }
    
    
        void disp() {
            listBox->Clear();
    
            int offset = scrollBar->GetThumbPosition();
    
            itemCount = getOpenNodeCount<FolderData>(_root);
            // スクロールバーの範囲とサムサイズを設定
            scrollBar->SetScrollbar(offset, visibleItemCount, itemCount, 5, true);
    
    
    
    
            _treeSlice.SetSliceInfo(visibleItemCount, offset);
            _treeSlice.GetSlice();
            _now_valid_items = _treeSlice.GetItems();
    
    
            // デバッグにposを表示
            std::string debugMessage = "pos: " + std::to_string(offset) + "\n";
            OutputDebugStringA(debugMessage.c_str());
            
            /////////////////////////////////////////////
            // 現在の表示範囲のアイテムを追加
            for (int i = 0; i < _now_valid_items.size(); i++) {
    
    
                std::string itemstr;
                // itemstr にアイテムの文字列を設定
                // hierarchy個のスペースを追加
                for (int j = 0; j < _now_valid_items[i].hierarchy; j++) {
                    itemstr += "   ";
                }
                auto node = _now_valid_items[i].node;
                if (node->isFolder()) {
                    std::dynamic_pointer_cast<Folder<FolderData>>(node)->getIsOpen() ? itemstr += "- " : itemstr += "+ ";
                }
                else {
                    itemstr += "  ";
                }
    
                itemstr += node->asString();
                listBox->Append(itemstr);// アイテムをリストに追加
    
            }
        }
        
        // スクロールバーのつまみ以外の部分をクリックしたとき
        void OnScrollPageUp(wxScrollEvent& event) {
    
            disp();
        }
    
        void OnScrollPageDown(wxScrollEvent& event) {
    
            disp();
        }
    
        void OnScrollThumbTrack(wxScrollEvent& event) {
            
            disp();
        }
    
        // 下方向クリック
        void OnScrollDown(wxScrollEvent& event) {
    
            disp();
        }
        // 上方向クリック
        void OnScrollUp(wxScrollEvent& event) {
    
            disp();
        }
    
        // リストボックスのアイテムをクリックした時のイベント
        void OnListBox(wxCommandEvent& event) {
            // 選択されたアイテムのインデックスを取得
            int index = listBox->GetSelection();
            if (index == wxNOT_FOUND) {
                return;
            }
    
            auto node = _now_valid_items[index].node;
            
            std::string debugMessage = "Selected: " + node->asString() + "\n";
            OutputDebugStringA(debugMessage.c_str());
    
            auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node);
    
            if (folder) {
                folder->setIsOpen(!folder->getIsOpen());// クリックしたフォルダをオープン
                disp();
            }
    
        }
    
    
    
        void OnSize(wxSizeEvent& event) {       
    
            visibleItemCount = GetVisibleItemCount(listBox);// 表示可能なアイテム個数を更新
    
            disp();
    
            // イベントを処理済みにする
            event.Skip();
        }
    
    
    };
    
    // ウィンドウ作成
    class MyFrame : public wxFrame
    {
        wxBoxSizer* mainSizer;
    
        ExList* exList; // スクロールバー付きリスト
    
    public:
    
        void PostCreate() {
    
    
            this->Layout(); // レイアウトの更新
        }
    
    
        MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
            : wxFrame(NULL, wxID_ANY, title, pos, size)
        
        {
    
            // メインSizer
            mainSizer = new wxBoxSizer(wxHORIZONTAL);
            this->SetSizer(mainSizer);
    
    
            // リストコントロール
            exList = new ExList(this);
            mainSizer->Add(exList, 1, wxEXPAND | wxALL, 5);
    
    
            // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
            // コンストラクタはウィンドウ生成イベント扱い
            CallAfter(&MyFrame::PostCreate);
    
    
        }
    
    private:
    };
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // wxWidgetsのアプリケーション作成
    class MyApp : public wxApp {
    public:
    
        virtual bool OnInit() {
            MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340));
            frame->Show(true);
    
            return true;
        }
    
    };
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // WinMainをマクロで定義
    wxIMPLEMENT_APP(MyApp);
    

    wxListBoxの表示内容を別途作成したスクロールバーでスクロールする

    #ifndef WX_PRECOMP
    #include <wx/wx.h>
    #endif
    
    #include <wx/gdicmn.h> // wxPointに必要
    #include <wx/frame.h>  // wxFrameに必要
    
    
    
    // wxBufferedPaintDC のinclude
    #include <wx/dcbuffer.h>
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    #include <string>
    #include <memory>
    #include <unordered_map>
     
    // リストボックスが一度に表示可能なアイテム数を取得
    int GetVisibleItemCount(wxListBox * listBox)
    {
        // リストボックスのDCを使って一行の高さを取得し
        // リストボックスのクライアントサイズから表示可能なアイテム数を計算する
    
    
        // リストボックスのクライアントサイズを取得
        wxSize clientSize = listBox->GetClientSize();
    
        // wxClientDCを使用してデフォルトのアイテム高さを取得
        wxClientDC dc(listBox);
        wxCoord textWidth, textHeight;
        // 指定した文字列の幅と高さを取得
        dc.GetTextExtent("TEXT", &textWidth, &textHeight);
    
        // 表示可能なアイテム数を計算
        int visibleItemCount = clientSize.GetHeight() / textHeight;
    
        return visibleItemCount;
    }
    
    // スクロールバー付きリスト
    class ExList : public wxPanel {
    
        int itemCount = 100; // アイテム数
        int visibleItemCount;// 一度に表示できるアイテム数
    
        wxBoxSizer* mainSizer;
        wxScrollBar* scrollBar;
    
        // リストコントロール
        wxListBox* listBox;
    
    
        void PostCreate() {
    
            visibleItemCount = GetVisibleItemCount(listBox);
    
            // スクロールバーの範囲とサムサイズを設定
            scrollBar->SetScrollbar(0, visibleItemCount, itemCount, 5, true);
    
            disp();
    
        }
    
    public:
        // コンストラクタ
        ExList(wxWindow* parent) : wxPanel(parent, wxID_ANY) {
            // メインSizer
            mainSizer = new wxBoxSizer(wxHORIZONTAL);
            this->SetSizer(mainSizer);
    
            // リストコントロール
            listBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr);
            mainSizer->Add(listBox, 1, wxEXPAND | wxALL, 5);
    
            // スクロールバー
            scrollBar = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
            mainSizer->Add(scrollBar, 0, wxEXPAND | wxALL, 5);
    
    
            this->Layout();
            CallAfter(&ExList::PostCreate);
    
            // スクロールバーのイベント
            scrollBar->Bind(wxEVT_SCROLL_PAGEUP, &ExList::OnScrollPageUp, this);
            scrollBar->Bind(wxEVT_SCROLL_PAGEDOWN, &ExList::OnScrollPageDown, this);
            scrollBar->Bind(wxEVT_SCROLL_THUMBTRACK, &ExList::OnScrollThumbTrack, this);
            scrollBar->Bind(wxEVT_SCROLL_LINEUP, &ExList::OnScrollUp, this);
            scrollBar->Bind(wxEVT_SCROLL_LINEDOWN, &ExList::OnScrollDown, this);
    
            // サイズイベント
            this->Bind(wxEVT_SIZE, &ExList::OnSize, this);
    
        }
    
        void disp() {
            listBox->Clear();
    
            int offset = scrollBar->GetThumbPosition();
    
            // デバッグにposを表示
            std::string debugMessage = "pos: " + std::to_string(offset) + "\n";
            OutputDebugStringA(debugMessage.c_str());
            
            /////////////////////////////////////////////
            // 現在の表示範囲のアイテムを追加
            for (int i = 0; i < visibleItemCount; i++) {
    
                int item_id = offset + i;
    
                // 番外のアイテムは *** で表示
                if (item_id >= itemCount) {
                    listBox->Append("***");
                    continue;
                }
    
                listBox->Append(wxString::Format("Item %d", item_id));
            }
        }
        
        void OnScrollPageUp(wxScrollEvent& event) {
            disp();
        }
    
        void OnScrollPageDown(wxScrollEvent& event) {
            disp();
        }
    
        void OnScrollThumbTrack(wxScrollEvent& event) {
            disp();
        }
    
        // 下方向クリック
        void OnScrollDown(wxScrollEvent& event) {
            disp();
        }
        // 上方向クリック
        void OnScrollUp(wxScrollEvent& event) {
            disp();
        }
    
        void OnSize(wxSizeEvent& event) {       
            // イベントを処理済みにする
            event.Skip();
        }
    
    
    };
    
    
    
    // ウィンドウ作成
    class MyFrame : public wxFrame
    {
        wxBoxSizer* mainSizer;
    
        ExList* exList; // スクロールバー付きリスト
    
    public:
    
        void PostCreate() {
    
    
            this->Layout(); // レイアウトの更新
        }
    
    
        MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
            : wxFrame(NULL, wxID_ANY, title, pos, size)
        
        {
    
            // メインSizer
            mainSizer = new wxBoxSizer(wxHORIZONTAL);
            this->SetSizer(mainSizer);
    
    
            // リストコントロール
            exList = new ExList(this);
            mainSizer->Add(exList, 1, wxEXPAND | wxALL, 5);
    
    
            // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
            // コンストラクタはウィンドウ生成イベント扱い
            CallAfter(&MyFrame::PostCreate);
    
    
        }
    
    private:
    };
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // wxWidgetsのアプリケーション作成
    class MyApp : public wxApp {
    public:
    
        virtual bool OnInit() {
            MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340));
            frame->Show(true);
    
            return true;
        }
    
    };
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // WinMainをマクロで定義
    wxIMPLEMENT_APP(MyApp);
    

    wxListBoxに表示可能な最大要素数を計算

    wxListBoxに一度に表示可能な最大アイテム数を求めたい。アイテムがある場合は、GetItemRect関数を使って要素の高さを取得できるので、コントロールのクライアント領域で割れば個数が得られる

    アイテムを追加して計算

    int GetVisibleItemCount2(wxListBox* listBox)
    {
        // アイテムを一つ追加し、そのアイテムの高さを取得
        listBox->Append("Sample Item");
        wxRect rect;
        listBox->GetItemRect(0, rect);
        listBox->Clear(); // 追加したアイテムを削除
    
        // アイテムの高さを取得
        wxSize clientSize = listBox->GetClientSize();
    
        // 表示可能なアイテム数を計算
        int visibleItemCount = clientSize.GetHeight() / rect.height;
        return visibleItemCount;
    
    }
    

    文字列の高さから算出

    アイテムがない場合、dc.GetTextExtentで文字列の高さを取得し、クライアントの高さをそれで割ることで取得できる。

    // リストボックスが一度に表示可能なアイテム数を取得
    int GetVisibleItemCount(wxListBox * listBox)
    {
        // リストボックスのDCを使って一行の高さを取得し
        // リストボックスのクライアントサイズから表示可能なアイテム数を計算する
    
    
        // リストボックスのクライアントサイズを取得
        wxSize clientSize = listBox->GetClientSize();
    
        // wxClientDCを使用してデフォルトのアイテム高さを取得
        wxClientDC dc(listBox);
        wxCoord textWidth, textHeight;
        // 指定した文字列の幅と高さを取得
        dc.GetTextExtent("TEXT", &textWidth, &textHeight);
    
        // 表示可能なアイテム数を計算
        int visibleItemCount = clientSize.GetHeight() / textHeight;
    
        return visibleItemCount;
    }
    

    呼び出し例

    // ウィンドウ作成
    class MyFrame : public wxFrame
    {
        // リストコントロール
        wxListBox* listBox;
        wxBoxSizer* mainSizer;
    
    public:
    
        void PostCreate() {
    
            int itemCount = listBox->GetCount();
    
            int count = GetVisibleItemCount(listBox);
    
            int count2 = GetVisibleItemCount2(listBox);
    
            assert(count == count2);
    
            // listBoxにcount個のアイテムを追加
            for (int i = 0; i < count; i++) {
                listBox->Append(wxString::Format("Item %d", i + 1));
            }
    
            // メッセージボックスでcountを表示
            wxMessageBox(wxString::Format("表示可能なアイテム数: %d %d", count,count2));
    
    
            this->Layout(); // レイアウトの更新
        }
    
    
        MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
            : wxFrame(NULL, wxID_ANY, title, pos, size)
        
        {
    
            // メインSizer
            mainSizer = new wxBoxSizer(wxHORIZONTAL);
            this->SetSizer(mainSizer);
    
    
            // リストコントロール
            listBox = new wxListBox(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0, nullptr);
            mainSizer->Add(listBox, 1, wxEXPAND | wxALL, 5);
    
    
            // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
            // コンストラクタはウィンドウ生成イベント扱い
            CallAfter(&MyFrame::PostCreate);
    
    
        }
    
    private:
    };
    

    ツリー構造を作成してコンソールから操作(2)

    いろいろ機能を追加した。

    p 番号 ... ノード表示

    s 番号 ... ツリーの一部を表示

    afh 番号 フォルダ名 ... 指定した番号の位置にフォルダを追加

    aih 番号 アイテム名 ... 指定した番号の位置にアイテムを追加

    afu 番号 フォルダ名 ... 指定した番号のフォルダの子要素としてフォルダを追加

    aif 番号 アイテム名 ... 指定した番号のフォルダの子要素としてアイテムを追加

    tree.hpp

    #ifndef TREE_H
    #define TREE_H
    
    #endif //TREE_H
    
    #include <algorithm>
    
    using namespace std::literals::string_literals;
    
    
    // ノードを継承したクラスに asString() メンバ関数があるかを判定するコンセプト
    template<typename T>
    concept HasAsString = requires(T a) {
        { a.asString() } -> std::same_as<std::string>;
    };

    // ノードの基底クラス
    class NodeBase {
    protected:
    public:
        std::shared_ptr<NodeBase> parent;
    
        virtual ~NodeBase() = default;
        virtual std::string asString() const = 0;  // 必ず実装させる仮想関数
        virtual std::shared_ptr<NodeBase> getParent() const { return parent; }
    };

    // フォルダクラス
    template <HasAsString T>
    class Folder : public NodeBase, public std::enable_shared_from_this < Folder<T> > {
        std::vector<std::shared_ptr<NodeBase>> children;  // 子ノードは NodeBase 型を保持
    
        T value;
        bool isOpen;
    public:
        Folder(const T& data) : value(data), isOpen(false) {}
    
        // 子ノードを追加するメソッド
        void add(const std::shared_ptr<NodeBase>& node) {
            children.push_back(node);
            node->parent = std::dynamic_pointer_cast<NodeBase>(this->shared_from_this());
        }
    
        const T& getName() const {
            return value;
        }
    
        std::string asString() const override {
            return value.asString();
        }
    
        const std::vector<std::shared_ptr<NodeBase>>& getChildren() const {
            return children;
        }
        std::vector<std::shared_ptr<NodeBase>>& getChildren() {
            return children;
        }
    
        bool getIsOpen() const {
            return isOpen;
        }
    
        void setIsOpen(bool open) {
            isOpen = open;
        }
    };
    

    // アイテムクラス
    // フォルダに入れるデータを保持するクラス
    template <HasAsString T>
    class Item : public NodeBase, public std::enable_shared_from_this < Item<T> >  {
        T value;
    public:
        Item(const T& data) : value(data) {}
    
        std::string asString() const override {
            return value.asString();
        }
    };
    


    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    
    // 全てのノードの数をカウントする関数
    template <HasAsString FT>
    int getAllNodeCount(const std::shared_ptr<NodeBase>& node) {
        int count = 1;  // 自分自身をカウント
    
        // フォルダの場合は、子ノードも再帰的にカウント
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            for (const auto& child : folder->getChildren()) {
                count += getAllNodeCount<FT>(child);
            }
        }
    
        return count;
    }
    

    // 開いているノードの数をカウントする関数
    template <HasAsString FT>
    int getOpenNodeCount(const std::shared_ptr<NodeBase>& node) {
        int count = 1;  // 自分自身をカウント
    
        // フォルダの場合、開いているかどうかを確認
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (folder->getIsOpen()) {
                // 開いている場合、子ノードも再帰的にカウント
                for (const auto& child : folder->getChildren()) {
                    count += getOpenNodeCount<FT>(child);
                }
            }
        }
    
        return count;
    }
    

    //////////////////////////////////////////////////// //////////////////////////////////////////////////// ////////////////////////////////////////////////////
     
    // 指定した index のノードを取得する関数の補助関数
    template <HasAsString FT>
    std::shared_ptr<NodeBase> getNodeHelper(const std::shared_ptr<NodeBase>& node, int& currentIndex, int targetIndex, bool open = false) {
        // 現在のノードをカウント
        if (currentIndex == targetIndex) {
            return node;
        }
        currentIndex++;
    
        // フォルダの場合は、開閉状態を確認し、再帰的に子ノードを探索
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (open || folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    auto result = getNodeHelper<FT>(child, currentIndex, targetIndex, open);
                    if (result) {
                        return result;
                    }
                }
            }
        }
    
        return nullptr; // 見つからなかった場合
    }
    

    // 指定した index のノードを取得する関数
    // ここでの index は、開いているノードのみでカウントする
    template <HasAsString FT>
    std::shared_ptr<NodeBase> getNodeOnlyOpen(const std::shared_ptr<NodeBase>& root, int targetIndex) {
        int currentIndex = 0; // ノードのカウント用
        return getNodeHelper<FT>(root, currentIndex, targetIndex,false);
    }
    

    ////////////////////////////////////////////////////
    //////////////////////////////////////////////////// ////////////////////////////////////////////////////
    // ノードを削除する関数
    template <HasAsString FT>
    void deleteNode(std::shared_ptr<NodeBase> node) {
        if (auto parent = node->getParent()) {
            if (auto parentFolder = std::dynamic_pointer_cast<Folder<FT>>(parent)) {
                auto& siblings = parentFolder->getChildren();
                siblings.erase(std::remove(siblings.begin(), siblings.end(), node), siblings.end());
            }
        }
    }
    

    // 指定したノードの管理情報を取得する関数
    // 戻り値 1 親ノード
    // 戻り値 2 posで指定したノードを持つ親ノードの、子ノードリストのインデクス
    template <HasAsString FT>
    std::pair< std::shared_ptr<NodeBase>,int> GetNodeListPos(std::shared_ptr<NodeBase> root,int pos) {
    
        // posの位置の現在のノードを取得
        auto now_node = getNodeOnlyOpen<FT>(root, pos);
    
        // now_nodeの親ノードを取得
        auto parent_node = now_node->getParent();
    
        // now_nodeの親ノードの子ノードリストを取得
        auto parent_folder = std::dynamic_pointer_cast<Folder<FT>>(parent_node);
        auto& siblings = parent_folder->getChildren();
    
        // now_nodeの親ノードの子ノードリストのインデクスを取得
        auto it = std::find(siblings.begin(), siblings.end(), now_node);
        int index = std::distance(siblings.begin(), it);
    
    
        return { parent_node,index };
    
    }
    

    template <HasAsString FT, HasAsString IT>
    void addNodeUnder(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node) {
    	// nodeをrootの最後に追加
    	auto folder = std::dynamic_pointer_cast<Folder<FT>>(root);
    	folder->getChildren().push_back(node);
    	node->parent = root;
    }
    

    template <HasAsString FT, HasAsString IT>
    void addNodeHere(std::shared_ptr<NodeBase> root, std::shared_ptr<NodeBase> node, int pos) {
        // ノードを追加する関数
        // 挿入位置は番号posで指定する
        // 新しく追加したアイテムが必ずpos番目になる
        // 必ず、追加前のposのノードと同じ階層に追加する
    
        // 番号posは開いているノードのみでカウントする
        // 例えばfolder21がノードを持っていても、folder21は開いていないのでその中のノードはカウントしない
        // 0 - folder1
        // 1   - folder2
        // 2      item21
        // 3      item22
        // 4      + folder21
        // 5   - folder3
        // 6      item31
        // 7      item32
        // 8      item33
    
        // getNode(folder1,3) == item21
        // この時、
        // 例1 addNode(NewFolder,2) で
        // 1   - folder2
        // 2      + NewFolder
        // 3      item21
        // 4      item22
    
    
        auto childinfo = GetNodeListPos<FT>(root, pos);
        std::shared_ptr<NodeBase> childlist = childinfo.first;
        auto index = childinfo.second;
    
        // nodeをchildlistのindex番に追加
        auto folder = std::dynamic_pointer_cast<Folder<FT>>(childlist);
        folder->getChildren().insert(folder->getChildren().begin() + index, node);
        node->parent = childlist;
    
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
     
    struct TreeSliceItem {
        int pos;
        std::shared_ptr<NodeBase> node;
        int hierarchy;
    };
    

    template <HasAsString FT>
    void GetTreeSliceTraverse(const std::shared_ptr<NodeBase>& node, int hierarchy, int& currentIndex, int from, int to, std::vector<TreeSliceItem>& slice) {
        if (currentIndex >= from && currentIndex <= to) {
            slice.push_back({ currentIndex, node, hierarchy });
        }
        currentIndex++;
    
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    GetTreeSliceTraverse<FT>(child, hierarchy + 1, currentIndex, from, to, slice);
                }
            }
        }
    }
    

    // ツリーから一部を取り出す関数
    template <HasAsString FT>
    std::vector< TreeSliceItem> GetTreeSlice(std::shared_ptr<NodeBase> root, int from,int to) {
        // 例えば、以下の状態の時、
        // 0 - folder1
        // 1   - folder2
        // 2      item21
        // 3      item22
        // 4      + folder21
        // 5   - folder3
        // 6      item31
        // 7      item32
        // 8      item33
    
        // GetTreeSlice(folder1,2,6) で
        // 2      item21
        // 3      item22
        // 4      + folder21
        // 5   - folder3
        // 6      item31
        // を取得
    
        std::vector<TreeSliceItem> slice;
        int currentIndex = 0;
        int currentHierarchy = 0;
    
        GetTreeSliceTraverse<FT>(root, currentHierarchy, currentIndex, from, to, slice);
    
        return slice;
    
    }
    

    main.cpp

    #include <iostream>
    #include <iomanip>
    #include <sstream>
    #include <memory>
    #include <vector>
    #include <string>
    
    
    #include "tree.hpp"
    
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    struct FolderData {
        std::string name;
        std::string asString() const {
            return name;
        }
    
        // コンストラクタ
        FolderData(const std::string& n) : name(n) {}
    
    
    };
    
    struct ItemData {
        std::string name;
        std::string asString() const {
            return name;
        }
    
        // コンストラクタ
        ItemData(const std::string& n) : name(n) {}
    
    };
    
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
     
    // ノードの番号を付けて表示するための補助関数
    template <HasAsString FT, HasAsString IT>
    void printTreeHelper(const std::shared_ptr<NodeBase>& node, int& lineNum, int indent = 0, bool open = false) {
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            // フォルダの場合
            std::cout << std::setw(2) << lineNum++ << std::string(indent+2, ' ') << (folder->getIsOpen() ? "- " : "+ ") << folder->asString() << "\n";
            if (open || folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    printTreeHelper<FT, IT>(child, lineNum, indent + 2, open);
                }
            }
        }
        else if (auto item = std::dynamic_pointer_cast<Item<IT>>(node)) {
            // アイテムの場合
            std::cout << std::setw(2) << lineNum++ << std::string(indent + 2, ' ') << item->asString() << "\n";
        }
    }
    
    
    // 指定したノード以下のツリーを表示する関数
    template <HasAsString FT, HasAsString IT>
    void printTree(const std::shared_ptr<NodeBase>& node, bool open = false) {
        int lineNum = 0;
        printTreeHelper<FT, IT>(node, lineNum, 0, open);
    }
    
          
    template <HasAsString FT, HasAsString IT>
    void printTreeSlice(const std::vector<TreeSliceItem>& slice) {
        for (const auto& item : slice) {
            if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(item.node)) {
                std::cout << std::setw(2) << item.pos << std::string(item.hierarchy * 2, ' ')
                    << (folder->getIsOpen() ? "- " : "+ ") << folder->asString() << "\n";
            }
            else if (auto itemNode = std::dynamic_pointer_cast<Item<IT>>(item.node)) {
                std::cout << std::setw(2) << item.pos << std::string(item.hierarchy * 2, ' ')
                    << itemNode->asString() << "\n";
            }
        }
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    
    // ノードの親ノードを表示する関数
    template <HasAsString FT, HasAsString IT>
    void printParentChain(const std::shared_ptr<NodeBase>& node) {
        auto current = node;
        while (current) {
            if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(current)) {
                std::cout << folder->asString() << " -> ";
            }
            else if (auto item = std::dynamic_pointer_cast<Item<IT>>(current)) {
                std::cout << item->asString() << " -> ";
            }
            current = current->getParent();
        }
        std::cout << "nullptr\n";  // ルートに到達したら終了
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    bool input_command(std::string* cmd, int* id, std::string* value) {
        std::string input;
        std::getline(std::cin, input);
        if (input.empty()) return false;
    
        std::istringstream iss(input);
        iss >> *cmd >> *id;
        if (iss >> std::ws && !iss.eof()) {
            std::getline(iss, *value);
            *value = value->substr(value->find_first_not_of(" \t")); // 先頭の空白を削除
        }
        else {
            *value = "";
        }
        return true;
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    int main() {
    
        // ツリー構造を作成
        auto root = std::make_shared<Folder<FolderData>>(FolderData{ "folder1" });
    
        auto folder2 = std::make_shared<Folder<FolderData>>(FolderData{ "folder2" });
        auto folder21 = std::make_shared<Folder<FolderData>>(FolderData{ "folder21" });
        auto item211 = std::make_shared<Item<ItemData>>(ItemData{ "item211" });
        auto folder22 = std::make_shared<Folder<FolderData>>(FolderData{ "folder22" });
        auto item221 = std::make_shared<Item<ItemData>>(ItemData{ "item221" });
        auto item222 = std::make_shared<Item<ItemData>>(ItemData{ "item222" });
        auto item223 = std::make_shared<Item<ItemData>>(ItemData{ "item223" });
    
        auto folder23 = std::make_shared<Folder<FolderData>>(FolderData{ "folder23" });
        auto folder24 = std::make_shared<Folder<FolderData>>(FolderData{ "folder24" });
        auto item241 = std::make_shared<Item<ItemData>>(ItemData{ "item241" });
        auto item242 = std::make_shared<Item<ItemData>>(ItemData{ "item242" });
    
        root->add(folder2);
        folder2->add(folder21);
        folder21->add(item211);
        folder2->add(folder22);
        folder22->add(item221);
        folder22->add(item222);
        folder22->add(item223);
        folder2->add(folder23);
        folder2->add(folder24);
        folder24->add(item241);
        folder24->add(item242);
    
    
        // フォルダの開閉状態を設定
        root->setIsOpen(true);
        folder2->setIsOpen(true);
        folder22->setIsOpen(true);
        folder24->setIsOpen(false);
    
        std::string cmd = " ";
        int id;
        std::string value;
        do {
    
            if (cmd == "*") { // 開閉
                auto node = getNodeOnlyOpen<FolderData>(root, id);
                auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node);
                folder->setIsOpen(!folder->getIsOpen());
            }
            else if (cmd == "p") { // 指定したノードを表示
    			auto node = getNodeOnlyOpen<FolderData>(root, id);
                std::cout<<node->asString() << std::endl;
            }
            else if (cmd == "afh") { // 指定した場所にフォルダを追加 ex. afh 1 folder3
                addNodeHere<FolderData, ItemData>(
                    root,
                    std::make_shared<Folder<FolderData>>(FolderData{ value }),
                    id
                );
            }
            else if (cmd == "aih") { // 指定した場所にアイテムを追加 ex. aih 3 item3
                addNodeHere<FolderData, ItemData>(
                    root,
                    std::make_shared<Item<ItemData>>(ItemData{ value }),
                    id
                );
            }
    		else if (cmd == "afu") { // 指定したフォルダの下にフォルダを追加 ex. afu 3 folder3
                auto selected = getNodeOnlyOpen<FolderData>(root, id);
    			addNodeUnder<FolderData, ItemData>(
                    selected,
    				std::make_shared<Folder<FolderData>>(FolderData{ value })
    			);
    		}
    		else if (cmd == "aiu") { // 指定したフォルダの下にアイテムを追加 ex. aiu 3 item3
                auto selected = getNodeOnlyOpen<FolderData>(root, id);
                addNodeUnder<FolderData, ItemData>(
                    selected,
    				std::make_shared<Item<ItemData>>(ItemData{ value })
    			);
    		}
            else if (cmd == "d") { // 削除
                auto node = getNodeOnlyOpen<FolderData>(root, id);
                deleteNode<FolderData>(node);
            }
            else if (cmd == "s") { // ツリーの一部を取得 ex. s 3
                auto slice = GetTreeSlice<FolderData>(root, id, id+4);
                printf("%d-%d\n", id, id + 4);
    			printTreeSlice<FolderData, ItemData>(slice);
                printf("\n");
            }
    
            printTree<FolderData, ItemData>(root, false);
    
            std::cout << "opennodes " << getOpenNodeCount<FolderData>(root) << std::endl;
            std::cout << "allnodes " << getAllNodeCount<FolderData>(root) << std::endl;
    
        } while (input_command(&cmd, &id, &value) == true);
    
    }
    

    実行例

     0  - folder1
     1    - folder2
     2      + folder21
     3      - folder22
     4        item221
     5        item222
     6        item223
     7      + folder23
     8      + folder24
    
    afh 5 NEW_FOLDER
     0  - folder1
     1    - folder2
     2      + folder21
     3      - folder22
     4        item221
     5        + NEW_FOLDER
     6        item222
     7        item223
     8      + folder23
     9      + folder24
    
    aiu 3 New_Item
     0  - folder1
     1    - folder2
     2      + folder21
     3      - folder22
     4        item221
     5        + NEW_FOLDER
     6        item222
     7        item223
     8        New_Item
     9      + folder23
    10      + folder24
    
    s 6
     6      item222
     7      item223
     8      New_Item
     9    + folder23
    10    + folder24
    

    ツリー構造を作成してコンソールから操作

    ツリー構造を作っている。動作確認のためコンソールから操作できるようにしている。

    #include <iostream>
    #include <iomanip>
    #include <sstream>
    #include <memory>
    #include <vector>
    #include <string>
    
    using namespace std::literals::string_literals;
    
    
    template<typename T>
    concept HasAsString = requires(T a) {
        { a.asString() } -> std::same_as<std::string>;
    };
    
    
    class NodeBase {
    protected:
    public:
        std::shared_ptr<NodeBase> parent;
    
        virtual ~NodeBase() = default;
        virtual std::string asString() const = 0;  // 必ず実装させる仮想関数
        virtual std::shared_ptr<NodeBase> getParent() const{return parent;}
    };
    
    template <HasAsString T>
    class Folder : public NodeBase, public std::enable_shared_from_this < Folder<T> > {
        std::vector<std::shared_ptr<NodeBase>> children;  // 子ノードは NodeBase 型を保持
    
        T value;
        bool isOpen;
    public:
        Folder(const T& data) : value(data), isOpen(false) {}
    
        // 子ノードを追加するメソッド
        void add(const std::shared_ptr<NodeBase>& node) {
            children.push_back(node);
            node->parent = std::dynamic_pointer_cast<NodeBase>(this->shared_from_this());
        }
    
        const T& getName() const {
            return value;
        }
    
        std::string asString() const override {
            return value.asString();
        }
    
        const std::vector<std::shared_ptr<NodeBase>>& getChildren() const {
            return children;
        }
        std::vector<std::shared_ptr<NodeBase>>& getChildren(){
            return children;
        }
    
        bool getIsOpen() const {
            return isOpen;
        }
    
        void setIsOpen(bool open) {
            isOpen = open;
        }
    };
    
    template <HasAsString T>
    class Item : public NodeBase {
        T value;
    public:
        Item(const T& data) : value(data) {}
    
        std::string asString() const override {
            return value.asString();
        }
    };
    
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    
    
    // ノードの番号を付けて表示するための補助関数
    template <HasAsString FT,HasAsString IT>
    void printTreeHelper(const std::shared_ptr<NodeBase>& node, int& lineNum, int indent = 0, bool open = false) {
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            // フォルダの場合
            std::cout << std::setw(2) << lineNum++ << std::string(indent, ' ') << (folder->getIsOpen() ? "- " : "+ ") << folder->asString() << "\n";
            if (open || folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    printTreeHelper<FT, IT>(child, lineNum, indent + 2, open); // ここでテンプレート引数を明示的に指定
                }
            }
        }
        else if (auto item = std::dynamic_pointer_cast<Item<IT>>(node)) {
            // アイテムの場合
            std::cout << std::setw(2) << lineNum++ << std::string(indent+1, ' ') << item->asString() << "\n";
        }
    }
    
    
    // 指定したノード以下のツリーを表示する関数
    template <HasAsString FT, HasAsString IT>
    void printTree(const std::shared_ptr<NodeBase>& node, bool open = false) {
        int lineNum = 1;
        printTreeHelper<FT,IT>(node, lineNum, 0, open);
    }
    
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    /////////////////////////////////////////////////////////////////
    
    
    // 全てのノードの数をカウントする関数
    template <HasAsString FT>
    int getAllNodeCount(const std::shared_ptr<NodeBase>& node) {
        int count = 1;  // 自分自身をカウント
    
        // フォルダの場合は、子ノードも再帰的にカウント
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            for (const auto& child : folder->getChildren()) {
                count += getAllNodeCount<FT>(child);
            }
        }
    
        return count;
    }
    
          
    // 開いているノードの数をカウントする関数
    template <HasAsString FT>
    int getOpenNodeCount(const std::shared_ptr<NodeBase>& node) {
        int count = 1;  // 自分自身をカウント
    
        // フォルダの場合、開いているかどうかを確認
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (folder->getIsOpen()) {
                // 開いている場合、子ノードも再帰的にカウント
                for (const auto& child : folder->getChildren()) {
                    count += getOpenNodeCount<FT>(child);
                }
            }
        }
    
        return count;
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    
    // 指定した index のノードを取得する関数
    template <HasAsString FT>
    std::shared_ptr<NodeBase> getNodeHelper(const std::shared_ptr<NodeBase>& node, int& currentIndex, int targetIndex, bool open = false) {
        // 現在のノードをカウント
        if (++currentIndex == targetIndex) {
            return node;
        }
    
        // フォルダの場合は、開閉状態を確認し、再帰的に子ノードを探索
        if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(node)) {
            if (open || folder->getIsOpen()) {
                for (const auto& child : folder->getChildren()) {
                    auto result = getNodeHelper<FT>(child, currentIndex, targetIndex, open);
                    if (result) {
                        return result;
                    }
                }
            }
        }
    
        return nullptr; // 見つからなかった場合
    }
    
    template <HasAsString FT>
    std::shared_ptr<NodeBase> getNode(const std::shared_ptr<NodeBase>& root, int targetIndex, bool open = false) {
        int currentIndex = 0; // ノードのカウント用
        return getNodeHelper<FT>(root, currentIndex, targetIndex, open);
    }
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    
    // ノードの親ノードを表示する関数
    template <HasAsString FT, HasAsString IT>
    void printParentChain(const std::shared_ptr<NodeBase>& node) {
        auto current = node;
        while (current) {
            if (auto folder = std::dynamic_pointer_cast<Folder<FT>>(current)) {
                std::cout << folder->asString() << " -> ";
            }
            else if (auto item = std::dynamic_pointer_cast<Item<IT>>(current)) {
                std::cout << item->asString() << " -> ";
            }
            current = current->getParent();
        }
        std::cout << "nullptr\n";  // ルートに到達したら終了
    }
    

    ////////////////////////////////////////////////////
    //////////////////////////////////////////////////// ////////////////////////////////////////////////////
    // ノードを削除する関数
    template <HasAsString FT>
    void deleteNode(std::shared_ptr<NodeBase> node) {
        if (auto parent = node->getParent()) {
            if (auto parentFolder = std::dynamic_pointer_cast<Folder<FT>>(parent)) {
                auto& siblings = parentFolder->getChildren();
                siblings.erase(std::remove(siblings.begin(), siblings.end(), node), siblings.end());
            }
        }
    }
    

    ////////////////////////////////////////////////////
    //////////////////////////////////////////////////// ////////////////////////////////////////////////////
     
    struct FolderData {
        std::string name;
        std::string asString() const {
            return name;
        }
    
        // コンストラクタ
        FolderData(const std::string& n) : name(n) {}
    
    
    };
    
    struct ItemData {
        std::string name;
        std::string asString() const {
            return name;
        }
    
        // コンストラクタ
        ItemData(const std::string& n) : name(n) {}
    
    };
    
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    ////////////////////////////////////////////////////
    
    bool input_command(char* cmd, int* id, std::string* value) {
        std::string input;
        std::getline(std::cin, input);
        if (input.empty()) return false;
    
        std::istringstream iss(input);
        iss >> *cmd >> *id;
        if (iss >> std::ws && !iss.eof()) {
            std::getline(iss, *value);
            *value = value->substr(value->find_first_not_of(" \t")); // 先頭の空白を削除
        }
        else {
            *value = "";
        }
        return true;
    }
    
    
    int main() {
    
        // ツリー構造を作成
        auto root = std::make_shared<Folder<FolderData>>(FolderData{ "folder1" });
    
        auto folder2 = std::make_shared<Folder<FolderData>>(FolderData{ "folder2" });
        auto folder21 = std::make_shared<Folder<FolderData>>(FolderData{ "folder21" });
        auto item211 = std::make_shared<Item<ItemData>>(ItemData{ "item211" });
        auto folder22 = std::make_shared<Folder<FolderData>>(FolderData{ "folder22" });
        auto item221 = std::make_shared<Item<ItemData>>(ItemData{ "item221"});
        auto item222 = std::make_shared<Item<ItemData>>(ItemData{ "item222"});
        auto item223 = std::make_shared<Item<ItemData>>(ItemData{ "item223"});
    
        auto folder23 = std::make_shared<Folder<FolderData>>(FolderData{ "folder23" });
        auto folder24 = std::make_shared<Folder<FolderData>>(FolderData{ "folder24" });
        auto item241 = std::make_shared<Item<ItemData>>(ItemData{ "item241" });
        auto item242 = std::make_shared<Item<ItemData>>(ItemData{ "item242" });
    
        root->add(folder2);
          folder2->add(folder21);
            folder21->add(item211);
          folder2->add(folder22);
            folder22->add(item221);
            folder22->add(item222);
            folder22->add(item223);
          folder2->add(folder23);
          folder2->add(folder24);
            folder24->add(item241);
            folder24->add(item242);
    
    
        // フォルダの開閉状態を設定
        root->setIsOpen(true);
        folder2->setIsOpen(true);
        folder22->setIsOpen(true);
        folder24->setIsOpen(false);
    
        char cmd=' ';
        int id;
        std::string value;
        do {
    
            switch (cmd) {
            case '*': // 開閉
            {
                auto node = getNode<FolderData>(root, id, false);
                auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node);
                folder->setIsOpen(!folder->getIsOpen());
            }
            break;
            case 'a': // 追加
            {
                auto node = getNode<FolderData>(root, id, false);
                auto folder = std::dynamic_pointer_cast<Folder<FolderData>>(node);
                auto item = std::make_shared<Item<ItemData>>(ItemData{ value });
                folder->add(item);
            }
            break;
            case 'd': // 削除
            {
                auto node = getNode<FolderData>(root, id, false);
                deleteNode<FolderData>(node);
            }
            break;
            }
    
            printTree<FolderData, ItemData>(root, false);
    
            std::cout << "opennodes " << getOpenNodeCount<FolderData>(root) << std::endl;
            std::cout << "allnodes " << getAllNodeCount<FolderData>(root) << std::endl;
    
        } while (input_command(&cmd, &id, &value) == true);
    
    }
    

    テスト

    最初に以下のようにツリーが表示される。フォルダの左側に+,-がついている。+は現在開いている記号、-は現在閉じている記号。

     1- folder1
     2  - folder2
     3    + folder21
     4    - folder22
     5       item221
     6       item222
     7       item223
     8    + folder23
     9    + folder24
    opennodes 9
    allnodes 12
    

    *でフォルダを開閉する。例えば「* 4」を入力すると以下のように表示される。

     1- folder1
     2  - folder2
     3    + folder21
     4    + folder22
     5    + folder23
     6    + folder24
    opennodes 6
    allnodes 12
    

    dでフォルダやアイテムを削除。 「d 5」で以下の結果になる

     1- folder1
     2  - folder2
     3    + folder21
     4    + folder22
     5    + folder24
    

    GridSizerを使用してリストを作成する

    一行にパネルを配置することでリストを作成する。

    ウィンドウサイズが変わるたびに、

    ① wxGridSizer::Clear(true);で現在のパネルをすべて削除し

    ② GridSizerを置いているwxBoxSizerから既存のwxGridSizerをwxBoxSizer::Detachで除外し、

    ③ 新しいwxGridSizerを作成し、この時に新しい行数を指定

    ④ 新しいwxGridSizerをGoxSizerにAddする

    ということをやる。どうやらwxGridSizerの分割数を更新する方法がないらしく、仕方がないので毎回作り直している。

    コード

    // 自作リストコントロール
    class MyList : public wxWindow
    {
    
        std::vector<int> _itemlist; // データリスト
    
        int row;
        int col = 1;
        int itemcount() { return _itemlist.size(); }
    
        int _itemHeight;
    
        wxScrollBar* _scroll;
        wxGridSizer* _gridSizer;
        wxBoxSizer* _boxSizer;
    public:
    
    
        // 縦に配置するアイテム一覧用のGridSizerを作成
        wxGridSizer* NewGridSizer() {
            int vgap = 1;// 垂直方向の間隔
            int hgap = 1;// 水平方向の間隔
    
            wxGridSizer* newGridSizer = new wxGridSizer(row, col, vgap, hgap);
    
            for (int i = 0; i < row * col; ++i)
            {
                //// 新しいパネルを作成
                wxPanel* panel = new wxPanel(this, wxID_ANY);
    
                //// パネルの背景色を設定
                panel->SetBackgroundColour(wxColour(200, 200, 100 * (i + 1) % 256));
    
                //// パネルをgridSizer追加
                newGridSizer->Add(panel, 1, wxEXPAND);
    
                // パネルにラベルを追加
                wxStaticText* label = new wxStaticText(panel, wxID_ANY, "");
                wxBoxSizer* labelboxSizer = new wxBoxSizer(wxHORIZONTAL);
                labelboxSizer->Add(label, 1, wxALIGN_CENTER);
                panel->SetSizer(labelboxSizer);
    
            }
    
            return newGridSizer;
        }
        
        
        // 水平分割のSizerを作成
        // 左側 ... GridSizer アイテム一覧
        // 右側 ... スクロールバー
        wxBoxSizer* NewBoxSizer(wxGridSizer* gridsizer) {
            // 水平分割のSizer作成
            auto boxSizer = new wxBoxSizer(wxHORIZONTAL);
            boxSizer->Add(gridsizer, 1, wxEXPAND);
    
            if (_scroll == nullptr) {
                //スクロールバー作成
                _scroll = new wxScrollBar(this, wxID_ANY, wxDefaultPosition, wxSize(20, -1), wxSB_VERTICAL);
                boxSizer->Add(_scroll, 0, wxEXPAND);
    
                _scroll->Bind(wxEVT_SCROLL_CHANGED, &MyList::OnScroll, this);
                _scroll->Bind(wxEVT_SCROLL_LINEUP, &MyList::OnScroll, this);
                _scroll->Bind(wxEVT_SCROLL_LINEDOWN, &MyList::OnScroll, this);
                _scroll->Bind(wxEVT_SCROLL_THUMBTRACK, &MyList::OnScroll, this);
            }
    
            return boxSizer;
        }
        
        
        // 表示行数を変更する
        void ResetRow() {
    
            wxGridSizer* newGridSizer = NewGridSizer();
    
            int scrollpos = 0;
    
            // リストを表示しているpanelをすべて削除
            if (_gridSizer != nullptr) {
                _gridSizer->Clear(true);
            }
    
            if (_boxSizer == nullptr) {
                _boxSizer = NewBoxSizer(newGridSizer);
            }
            else {
    
    
                // 既存の_gridSizerをboxSizerから削除
                _boxSizer->Detach(_gridSizer);
                _boxSizer->Detach(_scroll);
    
                // _gridSizerを削除
                delete _gridSizer;
                _gridSizer = nullptr;
    
                _boxSizer->Add(newGridSizer, 1, wxEXPAND);
                _boxSizer->Add(_scroll, 0, wxEXPAND);
    
            }
    
            _gridSizer = newGridSizer;
            
            scrollpos = _scroll->GetThumbPosition();
    
            SetSizer(_boxSizer);
    
            _scroll->SetScrollbar(
                scrollpos,    // position  スクロールバーの位置
                row,          // thumbSize つまみのサイズ。これは一度に画面上に表示する行数と等しいことが望ましい
                itemcount(),  // range   総行数
                2,            // pageSize  ページ単位でのスクロールを行ったときに移動する量
                true          // refresh   trueにするとスクロールバーの位置を更新する
            );
    
            // 表示を行う
            panel_update();
    
        }
    

        MyList(
            int ItemHeight,
            std::vector<int>& ITEM_LIST,
            wxWindow* parent,
            wxWindowID WINID,
            wxPoint point,
            wxSize size,
            long style,
            wxString name
        ) : wxWindow(parent, WINID,point,size,style,name)
        {
            _gridSizer = nullptr;
            _scroll = nullptr;
            _boxSizer = nullptr;
    
            _itemlist = ITEM_LIST;
    
    
            // アイテムの高さとウィンドウの高さから、一回に表示する件数を計算
            // この値はgridSizerの行数になる
            // ウィンドウの高さは変化するので、アイテム高さは完全に固定しないで、
            // ItemHeight以上、ItemHeight×2未満の範囲で調整する
            _itemHeight = ItemHeight;
    
            // ウィンドウサイズ変更時のイベントをBind
            Bind(wxEVT_SIZE, &MyList::OnSize, this);
    
    
            Centre();// ウィンドウを画面中央に表示
        }
    

        // ウィンドウサイズ変更時に呼び出されるイベントハンドラ
        void OnSize(wxSizeEvent& event)
        {
    
            // ウィンドウの高さから、一回に表示する件数を計算
            int newrow = event.GetSize().GetHeight() / _itemHeight;
            if (newrow < 1) newrow = 1;
    
            // 表示行数が変わったら、表示をリセット
            if (newrow != row) {
                row = newrow;
                ResetRow();
            }
    
            // 再配置
            _boxSizer->Layout();
    
    
            event.Skip();
        }
        
        
        // wxScrollBarをクリックしたときに呼び出されるイベントハンドラ
        void OnScroll(wxScrollEvent& event)
        {
            // スクロールバーの位置を取得
            int pos = event.GetPosition();
    
            // pos をデバッグ出力
            wxLogDebug(wxString::Format("pos = %d", pos));
    
            panel_update();
        }
    

        // panelの内容を更新する
        void panel_update() {
    
            // _scroll の位置を取得
            int pos = _scroll->GetThumbPosition();
    
            // offset = つまみの位置 = pos = 一番上に表示するアイテム番号
            // 表示アイテム = offset + i
            int offset = pos * col;
    
    
            // リストの上からi番目にアイテムを表示
            for (int i = 0; i < row * col; ++i)
            {
                // _gridSizerに登録されているpanelを取得
                wxPanel* panel = (wxPanel*)_gridSizer->GetItem(i)->GetWindow();
    
                // panel上にあるlabelを取得
                wxStaticText* label = (wxStaticText*)panel->GetChildren()[0];
    
                if ((offset + i) >= itemcount()) {
                    // アイテム数を超えたら *** を表示
                    label->SetLabelText("***");
                }
                else {
    
    
                    if (offset + i == itemcount() - 1) {
                        // 最後のアイテムなら (last item) を表示
                        label->SetLabelText(wxString::Format("Panel %d (last item)", _itemlist[offset + i]));
                    }
                    else {
                        // それ以外はアイテム番号を表示
                        label->SetLabelText(wxString::Format("Panel %d", _itemlist[offset + i]));
                    }
                }
            }
        }
    
    };
    

    使用例

    class MyFrame : public wxFrame
    {
    
    public:
    
        void PostCreate() {
    
            // セットするデータ
            std::vector<int> ImageList;
            for (size_t i = 0; i < 20; i++) {
                ImageList.push_back(i);
            }
    
            // リスト作成
            MyList* list = new MyList(20, ImageList,this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxBORDER_SIMPLE, "MyList");
    
            this->Layout(); // レイアウトの更新
    
        }
    
    
        MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
            : wxFrame(NULL, wxID_ANY, title, pos, size)
    
        {
            // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
            // コンストラクタはウィンドウ生成イベント扱い
            CallAfter(&MyFrame::PostCreate);
    
    
        }
    
    
    private:
    };
    

    SetConsoleOutputCPでプログラムからコマンドプロンプト上の文字コードを指定

    Windowsでコマンドプロンプトでutf8を表示するには、コードページを指定する必要がある。

    chcp 65001

    VC++から起動した場合chcpできないので、コードから変える必要がある。

    #include <cstdio>
    #include <windows.h>
    
    int main()
    {
        // 以下の二つはUTF-8を指定
        //SetConsoleOutputCP(CP_UTF8);
        //SetConsoleOutputCP(65001);
    
    
        UINT now = GetConsoleOutputCP();
    
        printf(u8"😊😎❤\n");
        printf("こんにちは\n");
        printf("\n");
    
        SetConsoleOutputCP(65001);
    
        printf(u8"😊😎❤\n");
        printf("こんにちは\n");
        printf("\n");
    
        SetConsoleOutputCP(now);
    
        printf(u8"😊😎❤\n");
        printf("こんにちは\n");
        printf("\n");
    }
    

    wxWidgetsのSizerを交換してレイアウトを変更する

    wxWidgetsでレイアウトを変更したい場合、

    ① 現在のSizerに登録されているウィジェットをバックアップ

    ② Sizerをクリアする(ただしそこに属しているウィジェットは削除しない)

    ③ 新しいSizerを作成

    ④ バックアップしたウィジェットを新しいSizerに設定

    ⑤ 新しいSizerをセット

    手順

    ① ウィジェットをバックアップ

    wxWindowList windowList;
    
    wxSizerItemList& itemList = currentSizer->GetChildren();
    // 現在のSizerからウィジェットを取り出してリストに保存
    for (wxSizerItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) {
        wxSizerItem* item = *it;
        if (item->IsWindow()) {
            wxWindow* window = item->GetWindow();
            windowList.push_back(window);
        }
    }
    

    ② Sizerをクリア

    // Sizerをクリア
    currentSizer->Clear(false);
    

    ③ 新しいSizerを作成

    // 新しいSizerを作成
    wxBoxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
    

    ④ バックアップしたウィジェットをリストア

    // ウィジェットを新しいSizerに追加
    for (wxWindowList::iterator it = windowList.begin(); it != windowList.end(); ++it) {
        wxWindow* window = *it;
        newSizer->Add(window, 1, wxALL | wxEXPAND, 0);
    }
    

    ⑤ 新しいSizerをセット

    // 親ウィジェットに新しいSizerを設定
    panelB->SetSizer(newSizer);
    

    テストコード

    // プリプロセッサに以下二つを追加
    // __WXMSW__
    // WXUSINGDLL
    
    // サブシステムをWindowsに設定(WinMainで呼び出すので)
    // Windows (/SUBSYSTEM:WINDOWS)
    
    #ifndef WX_PRECOMP
    #include <wx/wx.h>
    #endif
    
    #include <wx/gdicmn.h> // wxPointに必要
    #include <wx/frame.h>  // wxFrameに必要
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    #include <string>
    
    
    
    // ウィンドウ作成
    class MyFrame : public wxFrame
    {
    
    public:
    
        wxBoxSizer* sizerB;
        wxPanel* panelB;
    
        void PanelAB() {
    
            // パネルA,B作成
    
            // 交換ボタン配置用のパネル
            wxPanel* panelA = new wxPanel(this, wxID_ANY, wxPoint(0, 0), wxSize(200, 100));
            panelA->SetBackgroundColour(wxColour(255, 0, 0));
    
            // このパネルにパネルB1,B2を配置
            // これらのレイアウトを変更する
            panelB = new wxPanel(this, wxID_ANY, wxPoint(200, 0), wxSize(200, 100));
            panelB->SetBackgroundColour(wxColour(0, 0, 255));
    
    
            // パネルAにボタン作成
            wxButton* buttonA = new wxButton(panelA, wxID_ANY, "ButtonA", wxPoint(10, 10), wxSize(100, 50));
    
            // 画面を上下に分割し、上にpanelA、下にpanelBを配置
            wxBoxSizer* sizer = new wxBoxSizer(wxVERTICAL);
            sizer->Add(panelA, 0, wxEXPAND);
            sizer->Add(panelB, 1, wxEXPAND);
            SetSizer(sizer);
    
            // ボタンAを押したときのイベント
            buttonA->Bind(wxEVT_BUTTON, &MyFrame::OnButtonClicked, this);
    
        }
    
        void PanelB1B2() {
    
            // パネルBをsizerで左右に分割し、panelB1,panelB2を配置
            sizerB = new wxBoxSizer(wxHORIZONTAL);
            wxPanel* panelB1 = new wxPanel(panelB, wxID_ANY, wxPoint(0, 0), wxSize(100, 100));
            wxPanel* panelB2 = new wxPanel(panelB, wxID_ANY, wxPoint(0, 0), wxSize(100, 100));
            panelB1->SetBackgroundColour(wxColour(255, 0, 255));
            panelB2->SetBackgroundColour(wxColour(0, 255, 255));
            sizerB->Add(panelB1, 1, wxEXPAND);
            sizerB->Add(panelB2, 1, wxEXPAND);
            panelB->SetSizer(sizerB);
    
            // パネルB1にボタン作成
            wxButton* buttonB1 = new wxButton(panelB1, wxID_ANY, "ButtonB1", wxPoint(10, 10), wxSize(80, 50));
            wxButton* buttonB2 = new wxButton(panelB2, wxID_ANY, "ButtonB2", wxPoint(10, 10), wxSize(80, 50));
    
        }
    
        void PostCreate() {
    
            PanelAB();
    
            PanelB1B2();
    
            this->Layout(); // レイアウトの更新
    
        }
    
    
        // ボタンを押したときにSizerを交換
        void OnButtonClicked(wxCommandEvent& event) {
    
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
            ///////////////////////////////////////////////////////
    
            wxBoxSizer* currentSizer = sizerB;
            if (currentSizer) {
    
                wxWindowList windowList;
    
    
                wxSizerItemList& itemList = currentSizer->GetChildren();
                // 現在のSizerからウィジェットを取り出してリストに保存
                for (wxSizerItemList::iterator it = itemList.begin(); it != itemList.end(); ++it) {
                    wxSizerItem* item = *it;
                    if (item->IsWindow()) {
                        wxWindow* window = item->GetWindow();
                        windowList.push_back(window);
                    }
                }
    

                // Sizerをクリア
                currentSizer->Clear(false);

                // 新しいSizerを作成
                wxBoxSizer* newSizer = new wxBoxSizer(wxVERTICAL);
    

                // ウィジェットを新しいSizerに追加
                for (wxWindowList::iterator it = windowList.begin(); it != windowList.end(); ++it) {
                    wxWindow* window = *it;
                    newSizer->Add(window, 1, wxALL | wxEXPAND, 0);
                }
    

                // 親ウィジェットに新しいSizerを設定
                panelB->SetSizer(newSizer);
    
                this->Layout(); // レイアウトの更新
    
                sizerB = newSizer;
     
            }
    
        }
    
        MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size)
            : wxFrame(NULL, wxID_ANY, title, pos, size)
        
        {
            // CallAfter : 現在処理中のイベントが終わったらPostCreateを実行
            // コンストラクタはウィンドウ生成イベント扱い
            CallAfter(&MyFrame::PostCreate);
    
    
        }
    
    };
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // wxWidgetsのアプリケーション作成
    class MyApp : public wxApp {
    public:
    
        virtual bool OnInit() {
            MyFrame* frame = new MyFrame("Hello World", wxPoint(50, 50), wxSize(450, 340));
            frame->Show(true);
    
            return true;
        }
    
    };
    
    /////////////////////////////////////
    /////////////////////////////////////
    /////////////////////////////////////
    
    // WinMainをマクロで定義
    wxIMPLEMENT_APP(MyApp);